Commit 5293406d authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch 'feature-flags-prop-pocalypse' into 'master'

Convert Props to Injected Values for Feature Flags

See merge request gitlab-org/gitlab!45033
parents 61c55281 91f35980
...@@ -14,17 +14,6 @@ import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; ...@@ -14,17 +14,6 @@ import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
import Callout from '~/vue_shared/components/callout.vue'; import Callout from '~/vue_shared/components/callout.vue';
export default { export default {
cancelActionLabel: __('Close'),
modalTitle: s__('FeatureFlags|Configure feature flags'),
apiUrlLabelText: s__('FeatureFlags|API URL'),
apiUrlCopyText: __('Copy URL'),
instanceIdLabelText: s__('FeatureFlags|Instance ID'),
instanceIdCopyText: __('Copy ID'),
instanceIdRegenerateError: __('Unable to generate new instance ID'),
instanceIdRegenerateText: __(
'Regenerating the instance ID can break integration depending on the client you are using.',
),
instanceIdRegenerateActionLabel: __('Regenerate instance ID'),
components: { components: {
GlFormGroup, GlFormGroup,
GlFormInput, GlFormInput,
...@@ -42,18 +31,6 @@ export default { ...@@ -42,18 +31,6 @@ export default {
}, },
props: { props: {
helpClientLibrariesPath: {
type: String,
required: true,
},
helpClientExamplePath: {
type: String,
required: true,
},
apiUrl: {
type: String,
required: true,
},
instanceId: { instanceId: {
type: String, type: String,
required: true, required: true,
...@@ -76,7 +53,26 @@ export default { ...@@ -76,7 +53,26 @@ export default {
required: true, required: true,
}, },
}, },
inject: ['projectName', 'featureFlagsHelpPagePath'], inject: [
'projectName',
'featureFlagsHelpPagePath',
'unleashApiUrl',
'featureFlagsClientExampleHelpPagePath',
'featureFlagsClientLibrariesHelpPagePath',
],
translations: {
cancelActionLabel: __('Close'),
modalTitle: s__('FeatureFlags|Configure feature flags'),
apiUrlLabelText: s__('FeatureFlags|API URL'),
apiUrlCopyText: __('Copy URL'),
instanceIdLabelText: s__('FeatureFlags|Instance ID'),
instanceIdCopyText: __('Copy ID'),
instanceIdRegenerateError: __('Unable to generate new instance ID'),
instanceIdRegenerateText: __(
'Regenerating the instance ID can break integration depending on the client you are using.',
),
instanceIdRegenerateActionLabel: __('Regenerate instance ID'),
},
data() { data() {
return { return {
enteredProjectName: '', enteredProjectName: '',
...@@ -85,7 +81,7 @@ export default { ...@@ -85,7 +81,7 @@ export default {
computed: { computed: {
cancelActionProps() { cancelActionProps() {
return { return {
text: this.$options.cancelActionLabel, text: this.$options.translations.cancelActionLabel,
}; };
}, },
canRegenerateInstanceId() { canRegenerateInstanceId() {
...@@ -94,7 +90,7 @@ export default { ...@@ -94,7 +90,7 @@ export default {
regenerateInstanceIdActionProps() { regenerateInstanceIdActionProps() {
return this.canUserRotateToken return this.canUserRotateToken
? { ? {
text: this.$options.instanceIdRegenerateActionLabel, text: this.$options.translations.instanceIdRegenerateActionLabel,
attributes: [ attributes: [
{ {
category: 'secondary', category: 'secondary',
...@@ -129,7 +125,7 @@ export default { ...@@ -129,7 +125,7 @@ export default {
@primary.prevent="rotateToken" @primary.prevent="rotateToken"
> >
<template #modal-title> <template #modal-title>
{{ $options.modalTitle }} {{ $options.translations.modalTitle }}
</template> </template>
<p> <p>
<gl-sprintf <gl-sprintf
...@@ -140,7 +136,11 @@ export default { ...@@ -140,7 +136,11 @@ export default {
" "
> >
<template #docsLinkAnchored="{ content }"> <template #docsLinkAnchored="{ content }">
<gl-link :href="helpClientLibrariesPath" target="_blank" data-testid="help-client-link"> <gl-link
:href="featureFlagsClientLibrariesHelpPagePath"
target="_blank"
data-testid="help-client-link"
>
{{ content }} {{ content }}
</gl-link> </gl-link>
</template> </template>
...@@ -161,16 +161,18 @@ export default { ...@@ -161,16 +161,18 @@ export default {
" "
> >
<template #link="{ content }"> <template #link="{ content }">
<gl-link :href="helpClientExamplePath" target="_blank">{{ content }}</gl-link> <gl-link :href="featureFlagsClientExampleHelpPagePath" target="_blank">{{
content
}}</gl-link>
</template> </template>
</gl-sprintf> </gl-sprintf>
</callout> </callout>
<div class="form-group"> <div class="form-group">
<label for="api_url" class="label-bold">{{ $options.apiUrlLabelText }}</label> <label for="api_url" class="label-bold">{{ $options.translations.apiUrlLabelText }}</label>
<div class="input-group"> <div class="input-group">
<input <input
id="api_url" id="api_url"
:value="apiUrl" :value="unleashApiUrl"
readonly readonly
class="form-control" class="form-control"
type="text" type="text"
...@@ -178,8 +180,8 @@ export default { ...@@ -178,8 +180,8 @@ export default {
/> />
<span class="input-group-append"> <span class="input-group-append">
<modal-copy-button <modal-copy-button
:text="apiUrl" :text="unleashApiUrl"
:title="$options.apiUrlCopyText" :title="$options.translations.apiUrlCopyText"
:modal-id="modalId" :modal-id="modalId"
class="input-group-text" class="input-group-text"
/> />
...@@ -187,7 +189,9 @@ export default { ...@@ -187,7 +189,9 @@ export default {
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="instance_id" class="label-bold">{{ $options.instanceIdLabelText }}</label> <label for="instance_id" class="label-bold">{{
$options.translations.instanceIdLabelText
}}</label>
<div class="input-group"> <div class="input-group">
<input <input
id="instance_id" id="instance_id"
...@@ -207,7 +211,7 @@ export default { ...@@ -207,7 +211,7 @@ export default {
<div class="input-group-append"> <div class="input-group-append">
<modal-copy-button <modal-copy-button
:text="instanceId" :text="instanceId"
:title="$options.instanceIdCopyText" :title="$options.translations.instanceIdCopyText"
:modal-id="modalId" :modal-id="modalId"
:disabled="isRotating" :disabled="isRotating"
class="input-group-text" class="input-group-text"
...@@ -220,12 +224,12 @@ export default { ...@@ -220,12 +224,12 @@ export default {
class="text-danger d-flex align-items-center font-weight-normal mb-2" class="text-danger d-flex align-items-center font-weight-normal mb-2"
> >
<gl-icon name="warning" class="mr-1" /> <gl-icon name="warning" class="mr-1" />
<span>{{ $options.instanceIdRegenerateError }}</span> <span>{{ $options.translations.instanceIdRegenerateError }}</span>
</div> </div>
<callout <callout
v-if="canUserRotateToken" v-if="canUserRotateToken"
category="danger" category="danger"
:message="$options.instanceIdRegenerateText" :message="$options.translations.instanceIdRegenerateText"
/> />
<p v-if="canUserRotateToken" data-testid="prevent-accident-text"> <p v-if="canUserRotateToken" data-testid="prevent-accident-text">
<gl-sprintf <gl-sprintf
......
...@@ -34,51 +34,11 @@ export default { ...@@ -34,51 +34,11 @@ export default {
directives: { directives: {
GlModal: GlModalDirective, GlModal: GlModalDirective,
}, },
props: { inject: {
csrfToken: { newUserListPath: { default: '' },
type: String, newFeatureFlagPath: { default: '' },
required: true, canUserConfigure: { required: true },
}, featureFlagsLimitExceeded: { required: true },
featureFlagsClientLibrariesHelpPagePath: {
type: String,
required: true,
},
featureFlagsClientExampleHelpPagePath: {
type: String,
required: true,
},
featureFlagsLimit: {
type: String,
required: true,
},
featureFlagsLimitExceeded: {
type: Boolean,
required: false,
default: false,
},
rotateInstanceIdPath: {
type: String,
required: false,
default: '',
},
unleashApiUrl: {
type: String,
required: true,
},
canUserConfigure: {
type: Boolean,
required: true,
},
newFeatureFlagPath: {
type: String,
required: false,
default: '',
},
newUserListPath: {
type: String,
required: false,
default: '',
},
}, },
data() { data() {
const scope = getParameterByName('scope') || SCOPES.FEATURE_FLAG_SCOPE; const scope = getParameterByName('scope') || SCOPES.FEATURE_FLAG_SCOPE;
...@@ -234,9 +194,6 @@ export default { ...@@ -234,9 +194,6 @@ export default {
</gl-alert> </gl-alert>
<configure-feature-flags-modal <configure-feature-flags-modal
v-if="canUserConfigure" v-if="canUserConfigure"
:help-client-libraries-path="featureFlagsClientLibrariesHelpPagePath"
:help-client-example-path="featureFlagsClientExampleHelpPagePath"
:api-url="unleashApiUrl"
:instance-id="instanceId" :instance-id="instanceId"
:is-rotating="isRotating" :is-rotating="isRotating"
:has-rotate-error="hasRotateError" :has-rotate-error="hasRotateError"
...@@ -296,7 +253,6 @@ export default { ...@@ -296,7 +253,6 @@ export default {
> >
<feature-flags-table <feature-flags-table
v-if="shouldRenderFeatureFlags" v-if="shouldRenderFeatureFlags"
:csrf-token="csrfToken"
:feature-flags="featureFlags" :feature-flags="featureFlags"
@toggle-flag="toggleFeatureFlag" @toggle-flag="toggleFeatureFlag"
/> />
......
...@@ -18,15 +18,12 @@ export default { ...@@ -18,15 +18,12 @@ export default {
}, },
mixins: [glFeatureFlagMixin()], mixins: [glFeatureFlagMixin()],
props: { props: {
csrfToken: {
type: String,
required: true,
},
featureFlags: { featureFlags: {
type: Array, type: Array,
required: true, required: true,
}, },
}, },
inject: ['csrfToken'],
data() { data() {
return { return {
deleteFeatureFlagUrl: null, deleteFeatureFlagUrl: null,
......
...@@ -17,33 +17,33 @@ export default () => { ...@@ -17,33 +17,33 @@ export default () => {
projectId, projectId,
unleashApiInstanceId, unleashApiInstanceId,
rotateInstanceIdPath, rotateInstanceIdPath,
featureFlagsClientLibrariesHelpPagePath,
featureFlagsClientExampleHelpPagePath,
unleashApiUrl,
canUserAdminFeatureFlag,
newFeatureFlagPath,
newUserListPath,
featureFlagsLimitExceeded,
} = el.dataset; } = el.dataset;
return new Vue({ return new Vue({
el, el,
store: createStore({ endpoint, projectId, unleashApiInstanceId, rotateInstanceIdPath }), store: createStore({ endpoint, projectId, unleashApiInstanceId, rotateInstanceIdPath }),
provide() { provide: {
return {
projectName, projectName,
featureFlagsHelpPagePath, featureFlagsHelpPagePath,
errorStateSvgPath, errorStateSvgPath,
}; featureFlagsClientLibrariesHelpPagePath,
}, featureFlagsClientExampleHelpPagePath,
render(createElement) { unleashApiUrl,
return createElement(FeatureFlagsComponent, {
props: {
featureFlagsClientLibrariesHelpPagePath:
el.dataset.featureFlagsClientLibrariesHelpPagePath,
featureFlagsClientExampleHelpPagePath: el.dataset.featureFlagsClientExampleHelpPagePath,
unleashApiUrl: el.dataset.unleashApiUrl,
featureFlagsLimitExceeded: el.dataset.featureFlagsLimitExceeded,
featureFlagsLimit: el.dataset.featureFlagsLimit,
csrfToken: csrf.token, csrfToken: csrf.token,
canUserConfigure: el.dataset.canUserAdminFeatureFlag, canUserConfigure: canUserAdminFeatureFlag !== undefined,
newFeatureFlagPath: el.dataset.newFeatureFlagPath, newFeatureFlagPath,
newUserListPath: el.dataset.newUserListPath, newUserListPath,
featureFlagsLimitExceeded,
}, },
}); render(createElement) {
return createElement(FeatureFlagsComponent);
}, },
}); });
}; };
...@@ -8,12 +8,12 @@ describe('Configure Feature Flags Modal', () => { ...@@ -8,12 +8,12 @@ describe('Configure Feature Flags Modal', () => {
const provide = { const provide = {
projectName: 'fakeProjectName', projectName: 'fakeProjectName',
featureFlagsHelpPagePath: '/help/path', featureFlagsHelpPagePath: '/help/path',
featureFlagsClientLibrariesHelpPagePath: '/help/path/#flags',
featureFlagsClientExampleHelpPagePath: '/feature-flags#clientexample',
unleashApiUrl: '/api/url',
}; };
const propsData = { const propsData = {
helpClientLibrariesPath: '/help/path/#flags',
helpClientExamplePath: '/feature-flags#clientexample',
apiUrl: '/api/url',
instanceId: 'instance-id-token', instanceId: 'instance-id-token',
isRotating: false, isRotating: false,
hasRotateError: false, hasRotateError: false,
...@@ -82,7 +82,7 @@ describe('Configure Feature Flags Modal', () => { ...@@ -82,7 +82,7 @@ describe('Configure Feature Flags Modal', () => {
provide.featureFlagsHelpPagePath, provide.featureFlagsHelpPagePath,
); );
expect(wrapper.find('[data-testid="help-client-link"]').attributes('href')).toBe( expect(wrapper.find('[data-testid="help-client-link"]').attributes('href')).toBe(
propsData.helpClientLibrariesPath, provide.featureFlagsClientLibrariesHelpPagePath,
); );
}); });
......
...@@ -21,7 +21,6 @@ localVue.use(Vuex); ...@@ -21,7 +21,6 @@ localVue.use(Vuex);
describe('Feature flags', () => { describe('Feature flags', () => {
const mockData = { const mockData = {
canUserConfigure: true, canUserConfigure: true,
// canUserRotateToken: true,
csrfToken: 'testToken', csrfToken: 'testToken',
featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example', featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example',
featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients', featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients',
...@@ -31,6 +30,8 @@ describe('Feature flags', () => { ...@@ -31,6 +30,8 @@ describe('Feature flags', () => {
newFeatureFlagPath: 'feature-flags/new', newFeatureFlagPath: 'feature-flags/new',
newUserListPath: '/user-list/new', newUserListPath: '/user-list/new',
unleashApiUrl: `${TEST_HOST}/api/unleash`, unleashApiUrl: `${TEST_HOST}/api/unleash`,
projectName: 'fakeProjectName',
errorStateSvgPath: '/assets/illustrations/feature_flag.svg',
}; };
const mockState = { const mockState = {
...@@ -43,17 +44,12 @@ describe('Feature flags', () => { ...@@ -43,17 +44,12 @@ describe('Feature flags', () => {
let mock; let mock;
let store; let store;
const factory = (propsData = mockData, fn = shallowMount) => { const factory = (provide = mockData, fn = shallowMount) => {
store = createStore(mockState); store = createStore(mockState);
wrapper = fn(FeatureFlagsComponent, { wrapper = fn(FeatureFlagsComponent, {
localVue, localVue,
store, store,
propsData, provide,
provide: {
projectName: 'fakeProjectName',
errorStateSvgPath: '/assets/illustrations/feature_flag.svg',
featureFlagsHelpPagePath: '/help/feature-flags',
},
stubs: { stubs: {
FeatureFlagsTab, FeatureFlagsTab,
}, },
...@@ -87,13 +83,13 @@ describe('Feature flags', () => { ...@@ -87,13 +83,13 @@ describe('Feature flags', () => {
}); });
describe('when limit exceeded', () => { describe('when limit exceeded', () => {
const propsData = { ...mockData, featureFlagsLimitExceeded: true }; const provideData = { ...mockData, featureFlagsLimitExceeded: true };
beforeEach(done => { beforeEach(done => {
mock mock
.onGet(`${TEST_HOST}/endpoint.json`, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } }) .onGet(`${TEST_HOST}/endpoint.json`, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.reply(200, getRequestData, {}); .reply(200, getRequestData, {});
factory(propsData); factory(provideData);
setImmediate(done); setImmediate(done);
}); });
...@@ -130,7 +126,7 @@ describe('Feature flags', () => { ...@@ -130,7 +126,7 @@ describe('Feature flags', () => {
}); });
describe('without permissions', () => { describe('without permissions', () => {
const propsData = { const provideData = {
...mockData, ...mockData,
canUserConfigure: false, canUserConfigure: false,
canUserRotateToken: false, canUserRotateToken: false,
...@@ -142,7 +138,7 @@ describe('Feature flags', () => { ...@@ -142,7 +138,7 @@ describe('Feature flags', () => {
mock mock
.onGet(`${TEST_HOST}/endpoint.json`, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } }) .onGet(`${TEST_HOST}/endpoint.json`, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.reply(200, getRequestData, {}); .reply(200, getRequestData, {});
factory(propsData); factory(provideData);
setImmediate(done); setImmediate(done);
}); });
......
...@@ -38,7 +38,6 @@ const getDefaultProps = () => ({ ...@@ -38,7 +38,6 @@ const getDefaultProps = () => ({
], ],
}, },
], ],
csrfToken: 'fakeToken',
}); });
describe('Feature flag table', () => { describe('Feature flag table', () => {
...@@ -48,6 +47,9 @@ describe('Feature flag table', () => { ...@@ -48,6 +47,9 @@ describe('Feature flag table', () => {
const createWrapper = (propsData, opts = {}) => { const createWrapper = (propsData, opts = {}) => {
wrapper = shallowMount(FeatureFlagsTable, { wrapper = shallowMount(FeatureFlagsTable, {
propsData, propsData,
provide: {
csrfToken: 'fakeToken',
},
...opts, ...opts,
}); });
}; };
...@@ -220,7 +222,9 @@ describe('Feature flag table', () => { ...@@ -220,7 +222,9 @@ describe('Feature flag table', () => {
}, },
], ],
}; };
createWrapper(newVersionProps, { provide: { glFeatures: { featureFlagsNewVersion: true } } }); createWrapper(newVersionProps, {
provide: { csrfToken: 'fakeToken', glFeatures: { featureFlagsNewVersion: true } },
});
badges = wrapper.findAll('[data-testid="strategy-badge"]'); badges = wrapper.findAll('[data-testid="strategy-badge"]');
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment