Commit e1686c6f authored by Andrew Fontaine's avatar Andrew Fontaine

Remove Index Module from Feature Flags Store

This allows us to pass initialization data into the store and remove a
few simple props and actions completely.
parent 6617a2ba
<script>
import { createNamespacedHelpers } from 'vuex';
import { mapState, mapActions } from 'vuex';
import { isEmpty } from 'lodash';
import { GlButton, GlModalDirective, GlTabs } from '@gitlab/ui';
import { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE } from '../constants';
import FeatureFlagsTab from './feature_flags_tab.vue';
import FeatureFlagsTable from './feature_flags_table.vue';
import UserListsTable from './user_lists_table.vue';
import store from '../store';
import { s__ } from '~/locale';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import {
......@@ -17,12 +16,9 @@ import {
import ConfigureFeatureFlagsModal from './configure_feature_flags_modal.vue';
const { mapState, mapActions } = createNamespacedHelpers('index');
const SCOPES = { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE };
export default {
store,
components: {
FeatureFlagsTable,
UserListsTable,
......@@ -36,14 +32,6 @@ export default {
GlModal: GlModalDirective,
},
props: {
endpoint: {
type: String,
required: true,
},
projectId: {
type: String,
required: true,
},
csrfToken: {
type: String,
required: true,
......@@ -56,19 +44,10 @@ export default {
type: String,
required: true,
},
rotateInstanceIdPath: {
type: String,
required: false,
default: '',
},
unleashApiUrl: {
type: String,
required: true,
},
unleashApiInstanceId: {
type: String,
required: true,
},
canUserConfigure: {
type: Boolean,
required: true,
......@@ -144,23 +123,15 @@ export default {
},
},
created() {
this.setFeatureFlagsEndpoint(this.endpoint);
this.setFeatureFlagsOptions({ scope: this.scope, page: this.page });
this.setProjectId(this.projectId);
this.fetchFeatureFlags();
this.fetchUserLists();
this.setInstanceId(this.unleashApiInstanceId);
this.setInstanceIdEndpoint(this.rotateInstanceIdPath);
},
methods: {
...mapActions([
'setFeatureFlagsEndpoint',
'setFeatureFlagsOptions',
'fetchFeatureFlags',
'fetchUserLists',
'setInstanceIdEndpoint',
'setInstanceId',
'setProjectId',
'rotateInstanceId',
'toggleFeatureFlag',
'deleteUserList',
......
import Vue from 'vue';
import FeatureFlagsComponent from '~/feature_flags/components/feature_flags.vue';
import Vuex from 'vuex';
import csrf from '~/lib/utils/csrf';
import FeatureFlagsComponent from './components/feature_flags.vue';
import createStore from './store/modules/index';
export default () =>
new Vue({
el: '#feature-flags-vue',
components: {
FeatureFlagsComponent,
},
data() {
return {
dataset: document.querySelector(this.$options.el).dataset,
};
},
Vue.use(Vuex);
export default () => {
const el = document.querySelector('#feature-flags-vue');
const {
projectName,
featureFlagsHelpPagePath,
errorStateSvgPath,
endpoint,
projectId,
unleashApiInstanceId,
rotateInstanceIdPath,
} = el.dataset;
return new Vue({
el,
store: createStore({ endpoint, projectId, unleashApiInstanceId, rotateInstanceIdPath }),
provide() {
return {
projectName: this.dataset.projectName,
featureFlagsHelpPagePath: this.dataset.featureFlagsHelpPagePath,
errorStateSvgPath: this.dataset.errorStateSvgPath,
projectName,
featureFlagsHelpPagePath,
errorStateSvgPath,
};
},
render(createElement) {
return createElement('feature-flags-component', {
return createElement(FeatureFlagsComponent, {
props: {
endpoint: this.dataset.endpoint,
projectId: this.dataset.projectId,
featureFlagsClientLibrariesHelpPagePath: this.dataset
.featureFlagsClientLibrariesHelpPagePath,
featureFlagsClientExampleHelpPagePath: this.dataset.featureFlagsClientExampleHelpPagePath,
unleashApiUrl: this.dataset.unleashApiUrl,
unleashApiInstanceId: this.dataset.unleashApiInstanceId || '',
featureFlagsClientLibrariesHelpPagePath:
el.dataset.featureFlagsClientLibrariesHelpPagePath,
featureFlagsClientExampleHelpPagePath: el.dataset.featureFlagsClientExampleHelpPagePath,
unleashApiUrl: el.dataset.unleashApiUrl,
csrfToken: csrf.token,
canUserConfigure: this.dataset.canUserAdminFeatureFlag,
newFeatureFlagPath: this.dataset.newFeatureFlagPath,
rotateInstanceIdPath: this.dataset.rotateInstanceIdPath,
newUserListPath: this.dataset.newUserListPath,
canUserConfigure: el.dataset.canUserAdminFeatureFlag,
newFeatureFlagPath: el.dataset.newFeatureFlagPath,
newUserListPath: el.dataset.newUserListPath,
},
});
},
});
};
import Vue from 'vue';
import Vuex from 'vuex';
import indexModule from './modules/index';
Vue.use(Vuex);
export const createStore = () =>
new Vuex.Store({
modules: {
index: indexModule,
},
});
export default createStore();
......@@ -2,19 +2,9 @@ import Api from '~/api';
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
export const setFeatureFlagsEndpoint = ({ commit }, endpoint) =>
commit(types.SET_FEATURE_FLAGS_ENDPOINT, endpoint);
export const setFeatureFlagsOptions = ({ commit }, options) =>
commit(types.SET_FEATURE_FLAGS_OPTIONS, options);
export const setInstanceIdEndpoint = ({ commit }, endpoint) =>
commit(types.SET_INSTANCE_ID_ENDPOINT, endpoint);
export const setProjectId = ({ commit }, endpoint) => commit(types.SET_PROJECT_ID, endpoint);
export const setInstanceId = ({ commit }, instanceId) => commit(types.SET_INSTANCE_ID, instanceId);
export const fetchFeatureFlags = ({ state, dispatch }) => {
dispatch('requestFeatureFlags');
......
import Vuex from 'vuex';
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default {
namespaced: true,
actions,
mutations,
state: state(),
};
export default data =>
new Vuex.Store({
actions,
mutations,
state: state(data),
});
export const SET_FEATURE_FLAGS_ENDPOINT = 'SET_FEATURE_FLAGS_ENDPOINT';
export const SET_FEATURE_FLAGS_OPTIONS = 'SET_FEATURE_FLAGS_OPTIONS';
export const SET_INSTANCE_ID_ENDPOINT = 'SET_INSTANCE_ID_ENDPOINT';
export const SET_INSTANCE_ID = 'SET_INSTANCE_ID';
export const SET_PROJECT_ID = 'SET_PROJECT_ID';
export const REQUEST_FEATURE_FLAGS = 'REQUEST_FEATURE_FLAGS';
export const RECEIVE_FEATURE_FLAGS_SUCCESS = 'RECEIVE_FEATURE_FLAGS_SUCCESS';
......
......@@ -23,21 +23,9 @@ const createPaginationInfo = (state, headers) => {
};
export default {
[types.SET_FEATURE_FLAGS_ENDPOINT](state, endpoint) {
state.endpoint = endpoint;
},
[types.SET_FEATURE_FLAGS_OPTIONS](state, options = {}) {
state.options = options;
},
[types.SET_INSTANCE_ID_ENDPOINT](state, endpoint) {
state.rotateEndpoint = endpoint;
},
[types.SET_INSTANCE_ID](state, instance) {
state.instanceId = instance;
},
[types.SET_PROJECT_ID](state, project) {
state.projectId = project;
},
[types.REQUEST_FEATURE_FLAGS](state) {
state.isLoading = true;
},
......
import { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE } from '../../../constants';
export default () => ({
export default ({ endpoint, projectId, unleashApiInstanceId, rotateInstanceIdPath }) => ({
[FEATURE_FLAG_SCOPE]: [],
[USER_LIST_SCOPE]: [],
alerts: [],
......@@ -8,11 +8,11 @@ export default () => ({
pageInfo: { [FEATURE_FLAG_SCOPE]: {}, [USER_LIST_SCOPE]: {} },
isLoading: true,
hasError: false,
endpoint: null,
rotateEndpoint: null,
instanceId: '',
endpoint,
rotateEndpoint: rotateInstanceIdPath,
instanceId: unleashApiInstanceId,
isRotating: false,
hasRotateError: false,
options: {},
projectId: '',
projectId,
});
import initFeatureFlags from '~/feature_flags';
document.addEventListener('DOMContentLoaded', initFeatureFlags);
initFeatureFlags();
import { shallowMount } from '@vue/test-utils';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import MockAdapter from 'axios-mock-adapter';
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import Api from '~/api';
import { createStore } from '~/feature_flags/store';
import createStore from '~/feature_flags/store/modules/index';
import FeatureFlagsTab from '~/feature_flags/components/feature_flags_tab.vue';
import FeatureFlagsComponent from '~/feature_flags/components/feature_flags.vue';
import FeatureFlagsTable from '~/feature_flags/components/feature_flags_table.vue';
......@@ -14,19 +15,25 @@ import TablePagination from '~/vue_shared/components/pagination/table_pagination
import axios from '~/lib/utils/axios_utils';
import { getRequestData, userList } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Feature flags', () => {
const mockData = {
endpoint: `${TEST_HOST}/endpoint.json`,
csrfToken: 'testToken',
featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients',
featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example',
unleashApiUrl: `${TEST_HOST}/api/unleash`,
unleashApiInstanceId: 'oP6sCNRqtRHmpy1gw2-F',
canUserConfigure: true,
canUserRotateToken: true,
newFeatureFlagPath: 'feature-flags/new',
newUserListPath: '/user-list/new',
};
const mockState = {
endpoint: `${TEST_HOST}/endpoint.json`,
projectId: '8',
unleashApiInstanceId: 'oP6sCNRqtRHmpy1gw2-F',
};
let wrapper;
......@@ -34,8 +41,9 @@ describe('Feature flags', () => {
let store;
const factory = (propsData = mockData, fn = shallowMount) => {
store = createStore();
store = createStore(mockState);
wrapper = fn(FeatureFlagsComponent, {
localVue,
store,
propsData,
provide: {
......@@ -76,7 +84,6 @@ describe('Feature flags', () => {
describe('without permissions', () => {
const propsData = {
endpoint: `${TEST_HOST}/endpoint.json`,
csrfToken: 'testToken',
errorStateSvgPath: '/assets/illustrations/feature_flag.svg',
featureFlagsHelpPagePath: '/help/feature-flags',
......@@ -85,8 +92,6 @@ describe('Feature flags', () => {
featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients',
featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example',
unleashApiUrl: `${TEST_HOST}/api/unleash`,
unleashApiInstanceId: 'oP6sCNRqtRHmpy1gw2-F',
projectId: '8',
};
beforeEach(done => {
......@@ -134,7 +139,7 @@ describe('Feature flags', () => {
let emptyState;
beforeEach(async () => {
mock.onGet(mockData.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } }).reply(
mock.onGet(mockState.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } }).reply(
200,
{
feature_flags: [],
......@@ -154,8 +159,6 @@ describe('Feature flags', () => {
});
it('should render the empty state', async () => {
await axios.waitForAll();
emptyState = wrapper.find(GlEmptyState);
expect(emptyState.exists()).toBe(true);
});
......@@ -182,7 +185,7 @@ describe('Feature flags', () => {
describe('with paginated feature flags', () => {
beforeEach(done => {
mock
.onGet(mockData.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.onGet(mockState.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.replyOnce(200, getRequestData, {
'x-next-page': '2',
'x-page': '1',
......@@ -218,7 +221,7 @@ describe('Feature flags', () => {
const [flag] = table.props(FEATURE_FLAG_SCOPE);
table.vm.$emit('toggle-flag', flag);
expect(store.dispatch).toHaveBeenCalledWith('index/toggleFeatureFlag', flag);
expect(store.dispatch).toHaveBeenCalledWith('toggleFeatureFlag', flag);
});
it('renders configure button', () => {
......@@ -287,7 +290,7 @@ describe('Feature flags', () => {
describe('unsuccessful request', () => {
beforeEach(done => {
mock
.onGet(mockData.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.onGet(mockState.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.replyOnce(500, {});
Api.fetchFeatureFlagUserLists.mockRejectedValueOnce();
......
......@@ -7,10 +7,7 @@ import {
receiveFeatureFlagsSuccess,
receiveFeatureFlagsError,
fetchFeatureFlags,
setFeatureFlagsEndpoint,
setFeatureFlagsOptions,
setInstanceIdEndpoint,
setInstanceId,
rotateInstanceId,
requestRotateInstanceId,
receiveRotateInstanceIdSuccess,
......@@ -39,20 +36,7 @@ describe('Feature flags actions', () => {
let mockedState;
beforeEach(() => {
mockedState = state();
});
describe('setFeatureFlagsEndpoint', () => {
it('should commit SET_FEATURE_FLAGS_ENDPOINT mutation', done => {
testAction(
setFeatureFlagsEndpoint,
'feature_flags.json',
mockedState,
[{ type: types.SET_FEATURE_FLAGS_ENDPOINT, payload: 'feature_flags.json' }],
[],
done,
);
});
mockedState = state({});
});
describe('setFeatureFlagsOptions', () => {
......@@ -68,32 +52,6 @@ describe('Feature flags actions', () => {
});
});
describe('setInstanceIdEndpoint', () => {
it('should commit SET_INSTANCE_ID_ENDPOINT mutation', done => {
testAction(
setInstanceIdEndpoint,
'instance_id.json',
mockedState,
[{ type: types.SET_INSTANCE_ID_ENDPOINT, payload: 'instance_id.json' }],
[],
done,
);
});
});
describe('setInstanceId', () => {
it('should commit SET_INSTANCE_ID mutation', done => {
testAction(
setInstanceId,
'test_instance_id',
mockedState,
[{ type: types.SET_INSTANCE_ID, payload: 'test_instance_id' }],
[],
done,
);
});
});
describe('fetchFeatureFlags', () => {
let mock;
......
......@@ -9,15 +9,7 @@ describe('Feature flags store Mutations', () => {
let stateCopy;
beforeEach(() => {
stateCopy = state();
});
describe('SET_FEATURE_FLAGS_ENDPOINT', () => {
it('should set endpoint', () => {
mutations[types.SET_FEATURE_FLAGS_ENDPOINT](stateCopy, 'feature_flags.json');
expect(stateCopy.endpoint).toEqual('feature_flags.json');
});
stateCopy = state({});
});
describe('SET_FEATURE_FLAGS_OPTIONS', () => {
......@@ -27,23 +19,6 @@ describe('Feature flags store Mutations', () => {
expect(stateCopy.options).toEqual({ page: '1', scope: 'all' });
});
});
describe('SET_INSTANCE_ID_ENDPOINT', () => {
it('should set provided endpoint', () => {
mutations[types.SET_INSTANCE_ID_ENDPOINT](stateCopy, 'rotate_token.json');
expect(stateCopy.rotateEndpoint).toEqual('rotate_token.json');
});
});
describe('SET_INSTANCE_ID', () => {
it('should set provided token', () => {
mutations[types.SET_INSTANCE_ID](stateCopy, rotateData.token);
expect(stateCopy.instanceId).toEqual(rotateData.token);
});
});
describe('REQUEST_FEATURE_FLAGS', () => {
it('should set isLoading to true', () => {
mutations[types.REQUEST_FEATURE_FLAGS](stateCopy);
......
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