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