Commit cafbf4c0 authored by Filipa Lacerda's avatar Filipa Lacerda

Adds new and edit modules FF store

Adds two new modules to the feature
flags store to allow handling the creation
and edition of feature flags
parent 9a872a32
import Vue from 'vue';
import Vuex from 'vuex';
import indexModule from './modules/index';
import newModule from './modules/new';
import editModule from './modules/edit';
Vue.use(Vuex);
......@@ -8,6 +10,8 @@ export const createStore = () =>
new Vuex.Store({
modules: {
index: indexModule,
new: newModule,
edit: editModule,
},
});
......
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import createFlash from '~/flash';
import { __ } from '~/locale';
import { parseFeatureFlagsParams } from '../helpers';
/**
* Commits mutation to set the main endpoint
* @param {String} endpoint
*/
export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
/**
* Commits mutation to set the feature flag path.
* Used to redirect the user after form submission
*
* @param {String} path
*/
export const setPath = ({ commit }, path) => commit(types.SET_PATH, path);
/**
* Handles the edition of a feature flag.
*
* Will dispatch `requestUpdateFeatureFlag`
* Serializes the params and makes a put request
* Dispatches an action acording to the request status.
*
* @param {Object} params
*/
export const updateFeatureFlag = ({ state, dispatch }, params) => {
dispatch('requestUpdateFeatureFlag');
axios
.put(state.endpoint, { params: parseFeatureFlagsParams(params) })
.then(() => {
dispatch('receiveUpdateFeatureFlagSuccess');
visitUrl(state.path);
})
.catch(error => dispatch('receiveUpdateFeatureFlagError', error.response.data));
};
export const requestUpdateFeatureFlag = ({ commit }) => commit(types.REQUEST_UPDATE_FEATURE_FLAG);
export const receiveUpdateFeatureFlagSuccess = ({ commit }) =>
commit(types.RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS);
export const receiveUpdateFeatureFlagError = ({ commit }, error) =>
commit(types.RECEIVE_UPDATE_FEATURE_FLAG_ERROR, error);
/**
* Fetches the feature flag data for the edit form
*/
export const fetchFeatureFlag = ({ state, dispatch }) => {
dispatch('requestFeatureFlag');
axios
.get(state.endpoint)
.then(({ data }) => dispatch('receiveFeatureFlagSuccess', data))
.catch(() => dispatch('receiveFeatureFlagError'));
};
export const requestFeatureFlag = ({ commit }) => commit(types.REQUEST_FEATURE_FLAG);
export const receiveFeatureFlagSuccess = ({ commit }, response) =>
commit(types.RECEIVE_FEATURE_FLAG_SUCCESS, response);
export const receiveFeatureFlagError = ({ commit }) => {
commit(types.RECEIVE_FEATURE_FLAG_ERROR);
createFlash(__('Something went wrong on our end. Please try again!'));
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default {
namespaced: true,
actions,
mutations,
state: state(),
};
export const SET_ENDPOINT = 'SET_ENDPOINT';
export const SET_PATH = 'SET_PATH';
export const REQUEST_UPDATE_FEATURE_FLAG = 'REQUEST_UPDATE_FEATURE_FLAG';
export const RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS = 'RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS';
export const RECEIVE_UPDATE_FEATURE_FLAG_ERROR = 'RECEIVE_UPDATE_FEATURE_FLAG_ERROR';
export const REQUEST_FEATURE_FLAG = 'REQUEST_FEATURE_FLAG';
export const RECEIVE_FEATURE_FLAG_SUCCESS = 'RECEIVE_FEATURE_FLAG_SUCCESS';
export const RECEIVE_FEATURE_FLAG_ERROR = 'RECEIVE_FEATURE_FLAG_ERROR';
import * as types from './mutation_types';
export default {
[types.SET_ENDPOINT](state, endpoint) {
state.endpoint = endpoint;
},
[types.SET_PATH](state, path) {
state.path = path;
},
[types.REQUEST_FEATURE_FLAG](state) {
state.isLoading = true;
},
[types.RECEIVE_FEATURE_FLAG_SUCCESS](state, response) {
state.isLoading = false;
state.hasError = false;
state.name = response.name;
state.description = response.description;
state.scopes = state.scopes;
},
[types.RECEIVE_FEATURE_FLAG_ERROR](state) {
state.isLoading = false;
state.hasError = true;
},
[types.REQUEST_UPDATE_FEATURE_FLAG](state) {
state.isSendingRequest = true;
state.error = [];
},
[types.RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS](state) {
state.isSendingRequest = false;
},
[types.RECEIVE_UPDATE_FEATURE_FLAG_ERROR](state, error) {
state.isSendingRequest = false;
state.error = error.message;
},
};
export default () => ({
endpoint: null,
path: null,
isSendingRequest: false,
error: [],
name: null,
description: null,
scopes: null,
isLoading: false,
hasError: false,
});
// eslint-disable-next-line import/prefer-default-export
export const parseFeatureFlagsParams = params => ({
operations_feature_flags: {
name: params.name,
description: params.description,
active: true,
scopes_attributes: params.scopes.map(scope => ({
environment_scope: scope.name,
active: scope.active,
})),
},
});
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import { parseFeatureFlagsParams } from '../helpers';
/**
* Commits mutation to set the main endpoint
* @param {String} endpoint
*/
export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
/**
* Commits mutation to set the feature flag path.
* Used to redirect the user after form submission
*
* @param {String} path
*/
export const setPath = ({ commit }, path) => commit(types.SET_PATH, path);
/**
* Handles the creation of a new feature flag.
*
* Will dispatch `requestCreateFeatureFlag`
* Serializes the params and makes a post request
* Dispatches an action acording to the request status.
*
* @param {Object} params
*/
export const createFeatureFlag = ({ state, dispatch }, params) => {
dispatch('requestCreateFeatureFlag');
axios
.post(state.endpoint, { params: parseFeatureFlagsParams(params) })
.then(() => {
dispatch('receiveCreateFeatureFlagSuccess');
visitUrl(state.path);
})
.catch(error => dispatch('receiveCreateFeatureFlagError', error.response.data));
};
export const requestCreateFeatureFlag = ({ commit }) => commit(types.REQUEST_CREATE_FEATURE_FLAG);
export const receiveCreateFeatureFlagSuccess = ({ commit }) =>
commit(types.RECEIVE_CREATE_FEATURE_FLAG_SUCCESS);
export const receiveCreateFeatureFlagError = ({ commit }, error) =>
commit(types.RECEIVE_CREATE_FEATURE_FLAG_ERROR, error);
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default {
namespaced: true,
actions,
mutations,
state: state(),
};
export const SET_ENDPOINT = 'SET_ENDPOINT';
export const SET_PATH = 'SET_PATH';
export const REQUEST_CREATE_FEATURE_FLAG = 'REQUEST_CREATE_FEATURE_FLAG';
export const RECEIVE_CREATE_FEATURE_FLAG_SUCCESS = 'RECEIVE_CREATE_FEATURE_FLAG_SUCCESS';
export const RECEIVE_CREATE_FEATURE_FLAG_ERROR = 'RECEIVE_CREATE_FEATURE_FLAG_ERROR';
import * as types from './mutation_types';
export default {
[types.SET_ENDPOINT](state, endpoint) {
state.endpoint = endpoint;
},
[types.SET_PATH](state, path) {
state.path = path;
},
[types.REQUEST_CREATE_FEATURE_FLAG](state) {
state.isSendingRequest = true;
state.error = [];
},
[types.RECEIVE_CREATE_FEATURE_FLAG_SUCCESS](state) {
state.isSendingRequest = false;
},
[types.RECEIVE_CREATE_FEATURE_FLAG_ERROR](state, error) {
state.isSendingRequest = false;
state.error = error.message;
},
};
export default () => ({
endpoint: null,
path: null,
isSendingRequest: false,
error: [],
});
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import actions, {
setEndpoint,
setPath,
updateFeatureFlag,
requestUpdateFeatureFlag,
receiveUpdateFeatureFlagSuccess,
receiveUpdateFeatureFlagError,
fetchFeatureFlag,
requestFeatureFlag,
receiveFeatureFlagSuccess,
receiveFeatureFlagError,
} from 'ee/feature_flags/store/modules/edit/actions';
import state from 'ee/feature_flags/store/modules/edit/state';
import * as types from 'ee/feature_flags/store/modules/edit/mutation_types';
import testAction from 'spec/helpers/vuex_action_helper';
import { TEST_HOST } from 'spec/test_constants';
describe('Feature flags Edit Module actions', () => {
let mockedState;
beforeEach(() => {
mockedState = state();
});
describe('setEndpoint', () => {
it('should commit SET_ENDPOINT mutation', done => {
testAction(
setEndpoint,
'feature_flags.json',
mockedState,
[{ type: types.SET_ENDPOINT, payload: 'feature_flags.json' }],
[],
done,
);
});
});
describe('setPath', () => {
it('should commit SET_PATH mutation', done => {
testAction(
setPath,
'/feature_flags',
mockedState,
[{ type: types.SET_PATH, payload: '/feature_flags' }],
[],
done,
);
});
});
describe('updateFeatureFlag', () => {
let mock;
beforeEach(() => {
mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
mock = new MockAdapter(axios);
spyOnDependency(actions, 'visitUrl');
});
afterEach(() => {
mock.restore();
});
describe('success', () => {
it('dispatches requestUpdateFeatureFlag and receiveUpdateFeatureFlagSuccess ', done => {
mock
.onPut(mockedState.endpoint, {
params: {
operations_feature_flags: {
name: 'feature_flag',
description: 'feature flag',
active: true,
scopes_attributes: [{ environment_scope: '*', active: true }],
},
},
})
.replyOnce(200);
testAction(
updateFeatureFlag,
{
name: 'feature_flag',
description: 'feature flag',
scopes: [{ name: '*', active: true }],
},
mockedState,
[],
[
{
type: 'requestUpdateFeatureFlag',
},
{
type: 'receiveUpdateFeatureFlagSuccess',
},
],
done,
);
});
});
describe('error', () => {
it('dispatches requestUpdateFeatureFlag and receiveUpdateFeatureFlagError ', done => {
mock
.onPut(`${TEST_HOST}/endpoint.json`, {
params: {
operations_feature_flags: {
name: 'feature_flag',
description: 'feature flag',
active: true,
scopes_attributes: [{ environment_scope: '*', active: true }],
},
},
})
.replyOnce(500, { message: [] });
testAction(
updateFeatureFlag,
{
name: 'feature_flag',
description: 'feature flag',
scopes: [{ name: '*', active: true }],
},
mockedState,
[],
[
{
type: 'requestUpdateFeatureFlag',
},
{
type: 'receiveUpdateFeatureFlagError',
payload: { message: [] },
},
],
done,
);
});
});
});
describe('requestUpdateFeatureFlag', () => {
it('should commit REQUEST_UPDATE_FEATURE_FLAG mutation', done => {
testAction(
requestUpdateFeatureFlag,
null,
mockedState,
[{ type: types.REQUEST_UPDATE_FEATURE_FLAG }],
[],
done,
);
});
});
describe('receiveUpdateFeatureFlagSuccess', () => {
it('should commit RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS mutation', done => {
testAction(
receiveUpdateFeatureFlagSuccess,
null,
mockedState,
[
{
type: types.RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS,
},
],
[],
done,
);
});
});
describe('receiveUpdateFeatureFlagError', () => {
it('should commit RECEIVE_UPDATE_FEATURE_FLAG_ERROR mutation', done => {
testAction(
receiveUpdateFeatureFlagError,
'There was an error',
mockedState,
[{ type: types.RECEIVE_UPDATE_FEATURE_FLAG_ERROR, payload: 'There was an error' }],
[],
done,
);
});
});
describe('fetchFeatureFlag', () => {
let mock;
beforeEach(() => {
mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('success', () => {
it('dispatches requestFeatureFlag and receiveFeatureFlagSuccess ', done => {
mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { id: 1 });
testAction(
fetchFeatureFlag,
{ id: 1 },
mockedState,
[],
[
{
type: 'requestFeatureFlag',
},
{
type: 'receiveFeatureFlagSuccess',
payload: { id: 1 },
},
],
done,
);
});
});
describe('error', () => {
it('dispatches requestFeatureFlag and receiveUpdateFeatureFlagError ', done => {
mock.onGet(`${TEST_HOST}/endpoint.json`, {}).replyOnce(500, {});
testAction(
fetchFeatureFlag,
null,
mockedState,
[],
[
{
type: 'requestFeatureFlag',
},
{
type: 'receiveFeatureFlagError',
},
],
done,
);
});
});
});
describe('requestFeatureFlag', () => {
it('should commit REQUEST_FEATURE_FLAG mutation', done => {
testAction(
requestFeatureFlag,
null,
mockedState,
[{ type: types.REQUEST_FEATURE_FLAG }],
[],
done,
);
});
});
describe('receiveFeatureFlagSuccess', () => {
it('should commit RECEIVE_FEATURE_FLAG_SUCCESS mutation', done => {
testAction(
receiveFeatureFlagSuccess,
{ id: 1 },
mockedState,
[{ type: types.RECEIVE_FEATURE_FLAG_SUCCESS, payload: { id: 1 } }],
[],
done,
);
});
});
describe('receiveFeatureFlagError', () => {
it('should commit RECEIVE_FEATURE_FLAG_ERROR mutation', done => {
testAction(
receiveFeatureFlagError,
null,
mockedState,
[
{
type: types.RECEIVE_FEATURE_FLAG_ERROR,
},
],
[],
done,
);
});
});
});
import state from 'ee/feature_flags/store/modules/edit/state';
import mutations from 'ee/feature_flags/store/modules/edit/mutations';
import * as types from 'ee/feature_flags/store/modules/edit/mutation_types';
describe('Feature flags Edit Module Mutations', () => {
let stateCopy;
beforeEach(() => {
stateCopy = state();
});
describe('SET_ENDPOINT', () => {
it('should set endpoint', () => {
mutations[types.SET_ENDPOINT](stateCopy, 'feature_flags.json');
expect(stateCopy.endpoint).toEqual('feature_flags.json');
});
});
describe('SET_PATH', () => {
it('should set provided options', () => {
mutations[types.SET_PATH](stateCopy, 'feature_flags');
expect(stateCopy.path).toEqual('feature_flags');
});
});
describe('REQUEST_FEATURE_FLAG', () => {
it('should set isLoading to true', () => {
mutations[types.REQUEST_FEATURE_FLAG](stateCopy);
expect(stateCopy.isLoading).toEqual(true);
});
it('should set error to an empty array', () => {
mutations[types.REQUEST_FEATURE_FLAG](stateCopy);
expect(stateCopy.error).toEqual([]);
});
});
describe('RECEIVE_FEATURE_FLAG_SUCCESS', () => {
const data = {
name: '*',
description: 'All environments',
scopes: [{ id: 1 }],
};
beforeEach(() => {
mutations[types.RECEIVE_FEATURE_FLAG_SUCCESS](stateCopy, data);
});
it('should set isLoading to false', () => {
expect(stateCopy.isLoading).toEqual(false);
});
it('should set hasError to false', () => {
expect(stateCopy.hasError).toEqual(false);
});
it('should set name with the provided one', () => {
expect(stateCopy.name).toEqual(data.name);
});
it('should set description with the provided one', () => {
expect(stateCopy.description).toEqual(data.description);
});
it('should set scope with the provided one', () => {
expect(stateCopy.scope).toEqual(data.scope);
});
});
describe('RECEIVE_FEATURE_FLAG_ERROR', () => {
beforeEach(() => {
mutations[types.RECEIVE_FEATURE_FLAG_ERROR](stateCopy);
});
it('should set isLoading to false', () => {
expect(stateCopy.isLoading).toEqual(false);
});
it('should set hasError to true', () => {
expect(stateCopy.hasError).toEqual(true);
});
});
describe('REQUEST_UPDATE_FEATURE_FLAG', () => {
beforeEach(() => {
mutations[types.REQUEST_UPDATE_FEATURE_FLAG](stateCopy);
});
it('should set isSendingRequest to true', () => {
expect(stateCopy.isSendingRequest).toEqual(true);
});
it('should set error to an empty array', () => {
expect(stateCopy.error).toEqual([]);
});
});
describe('RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS', () => {
it('should set isSendingRequest to false', () => {
mutations[types.RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS](stateCopy);
expect(stateCopy.isSendingRequest).toEqual(false);
});
});
describe('RECEIVE_UPDATE_FEATURE_FLAG_ERROR', () => {
beforeEach(() => {
mutations[types.RECEIVE_UPDATE_FEATURE_FLAG_ERROR](stateCopy, {
message: ['Name is required'],
});
});
it('should set isSendingRequest to false', () => {
expect(stateCopy.isSendingRequest).toEqual(false);
});
it('should set error to the given message', () => {
expect(stateCopy.error).toEqual(['Name is required']);
});
});
});
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import actions, {
setEndpoint,
setPath,
createFeatureFlag,
requestCreateFeatureFlag,
receiveCreateFeatureFlagSuccess,
receiveCreateFeatureFlagError,
} from 'ee/feature_flags/store/modules/new/actions';
import state from 'ee/feature_flags/store/modules/new/state';
import * as types from 'ee/feature_flags/store/modules/new/mutation_types';
import testAction from 'spec/helpers/vuex_action_helper';
import { TEST_HOST } from 'spec/test_constants';
describe('Feature flags New Module Actions', () => {
let mockedState;
beforeEach(() => {
mockedState = state();
});
describe('setEndpoint', () => {
it('should commit SET_ENDPOINT mutation', done => {
testAction(
setEndpoint,
'feature_flags.json',
mockedState,
[{ type: types.SET_ENDPOINT, payload: 'feature_flags.json' }],
[],
done,
);
});
});
describe('setPath', () => {
it('should commit SET_PATH mutation', done => {
testAction(
setPath,
'/feature_flags',
mockedState,
[{ type: types.SET_PATH, payload: '/feature_flags' }],
[],
done,
);
});
});
describe('createFeatureFlag', () => {
let mock;
beforeEach(() => {
mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
mock = new MockAdapter(axios);
spyOnDependency(actions, 'visitUrl');
});
afterEach(() => {
mock.restore();
});
describe('success', () => {
it('dispatches requestCreateFeatureFlag and receiveCreateFeatureFlagSuccess ', done => {
mock
.onPost(`${TEST_HOST}/endpoint.json`, {
params: {
operations_feature_flags: {
name: 'feature_flag',
description: 'feature flag',
active: true,
scopes_attributes: [{ environment_scope: '*', active: true }],
},
},
})
.replyOnce(200);
testAction(
createFeatureFlag,
{
name: 'feature_flag',
description: 'feature flag',
scopes: [{ name: '*', active: true }],
},
mockedState,
[],
[
{
type: 'requestCreateFeatureFlag',
},
{
type: 'receiveCreateFeatureFlagSuccess',
},
],
done,
);
});
});
describe('error', () => {
it('dispatches requestCreateFeatureFlag and receiveCreateFeatureFlagError ', done => {
mock
.onPost(`${TEST_HOST}/endpoint.json`, {
params: {
operations_feature_flags: {
name: 'feature_flag',
description: 'feature flag',
active: true,
scopes_attributes: [{ environment_scope: '*', active: true }],
},
},
})
.replyOnce(500, { message: [] });
testAction(
createFeatureFlag,
{
name: 'feature_flag',
description: 'feature flag',
scopes: [{ name: '*', active: true }],
},
mockedState,
[],
[
{
type: 'requestCreateFeatureFlag',
},
{
type: 'receiveCreateFeatureFlagError',
payload: { message: [] },
},
],
done,
);
});
});
});
describe('requestCreateFeatureFlag', () => {
it('should commit REQUEST_CREATE_FEATURE_FLAG mutation', done => {
testAction(
requestCreateFeatureFlag,
null,
mockedState,
[{ type: types.REQUEST_CREATE_FEATURE_FLAG }],
[],
done,
);
});
});
describe('receiveCreateFeatureFlagSuccess', () => {
it('should commit RECEIVE_CREATE_FEATURE_FLAG_SUCCESS mutation', done => {
testAction(
receiveCreateFeatureFlagSuccess,
null,
mockedState,
[
{
type: types.RECEIVE_CREATE_FEATURE_FLAG_SUCCESS,
},
],
[],
done,
);
});
});
describe('receiveCreateFeatureFlagError', () => {
it('should commit RECEIVE_CREATE_FEATURE_FLAG_ERROR mutation', done => {
testAction(
receiveCreateFeatureFlagError,
'There was an error',
mockedState,
[{ type: types.RECEIVE_CREATE_FEATURE_FLAG_ERROR, payload: 'There was an error' }],
[],
done,
);
});
});
});
import state from 'ee/feature_flags/store/modules/new/state';
import mutations from 'ee/feature_flags/store/modules/new/mutations';
import * as types from 'ee/feature_flags/store/modules/new/mutation_types';
describe('Feature flags New Module Mutations', () => {
let stateCopy;
beforeEach(() => {
stateCopy = state();
});
describe('SET_ENDPOINT', () => {
it('should set endpoint', () => {
mutations[types.SET_ENDPOINT](stateCopy, 'feature_flags.json');
expect(stateCopy.endpoint).toEqual('feature_flags.json');
});
});
describe('SET_PATH', () => {
it('should set provided options', () => {
mutations[types.SET_PATH](stateCopy, 'feature_flags');
expect(stateCopy.path).toEqual('feature_flags');
});
});
describe('REQUEST_CREATE_FEATURE_FLAG', () => {
it('should set isSendingRequest to true', () => {
mutations[types.REQUEST_CREATE_FEATURE_FLAG](stateCopy);
expect(stateCopy.isSendingRequest).toEqual(true);
});
it('should set error to an empty array', () => {
mutations[types.REQUEST_CREATE_FEATURE_FLAG](stateCopy);
expect(stateCopy.error).toEqual([]);
});
});
describe('RECEIVE_CREATE_FEATURE_FLAG_SUCCESS', () => {
it('should set isSendingRequest to false', () => {
mutations[types.RECEIVE_CREATE_FEATURE_FLAG_SUCCESS](stateCopy);
expect(stateCopy.isSendingRequest).toEqual(false);
});
});
describe('RECEIVE_CREATE_FEATURE_FLAG_ERROR', () => {
beforeEach(() => {
mutations[types.RECEIVE_CREATE_FEATURE_FLAG_ERROR](stateCopy, {
message: ['Name is required'],
});
});
it('should set isSendingRequest to false', () => {
expect(stateCopy.isSendingRequest).toEqual(false);
});
it('should set hasError to true', () => {
expect(stateCopy.error).toEqual(['Name is required']);
});
});
});
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