Commit cebc9ec1 authored by Enrique Alcántara's avatar Enrique Alcántara Committed by Fatih Acet

Cluster dropdown Vuex store

Abstract cluster dropdown data fetching pattern
into an independent Vuex store module
parent cf10130a
<script> <script>
import { mapActions, mapState } from 'vuex'; import { createNamespacedHelpers, mapState, mapActions } from 'vuex';
import RegionDropdown from './region_dropdown.vue'; import RegionDropdown from './region_dropdown.vue';
import RoleNameDropdown from './role_name_dropdown.vue'; import RoleNameDropdown from './role_name_dropdown.vue';
import SecurityGroupDropdown from './security_group_dropdown.vue'; import SecurityGroupDropdown from './security_group_dropdown.vue';
import SubnetDropdown from './subnet_dropdown.vue'; import SubnetDropdown from './subnet_dropdown.vue';
import VPCDropdown from './vpc_dropdown.vue'; import VpcDropdown from './vpc_dropdown.vue';
const { mapState: mapRegionsState, mapActions: mapRegionsActions } = createNamespacedHelpers(
'regions',
);
export default { export default {
components: { components: {
...@@ -13,31 +16,39 @@ export default { ...@@ -13,31 +16,39 @@ export default {
RoleNameDropdown, RoleNameDropdown,
SecurityGroupDropdown, SecurityGroupDropdown,
SubnetDropdown, SubnetDropdown,
VPCDropdown, VpcDropdown,
}, },
computed: { computed: {
...mapState(['isLoadingRegions', 'loadingRegionsError', 'regions', 'selectedRegion']), ...mapState(['selectedRegion']),
...mapRegionsState({
regions: 'items',
isLoadingRegions: 'isLoadingItems',
loadingRegionsError: 'loadingItemsError',
}),
}, },
mounted() { mounted() {
this.fetchRegions(); this.fetchRegions();
}, },
methods: { methods: {
...mapActions(['fetchRegions', 'setRegion']), ...mapActions(['setRegion']),
...mapRegionsActions({
fetchRegions: 'fetchItems',
}),
}, },
}; };
</script> </script>
<template> <template>
<form name="eks-cluster-configuration-form"> <form name="eks-cluster-configuration-form">
<div class="form-group"> <div class="form-group">
<label class="label-bold" name="role" for="eks-role"> <label class="label-bold" name="role" for="eks-role">{{
{{ s__('ClusterIntegration|Role name') }} s__('ClusterIntegration|Role name')
</label> }}</label>
<role-name-dropdown /> <role-name-dropdown />
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="label-bold" name="role" for="eks-role"> <label class="label-bold" name="role" for="eks-role">{{
{{ s__('ClusterIntegration|Region') }} s__('ClusterIntegration|Region')
</label> }}</label>
<region-dropdown <region-dropdown
:value="selectedRegion" :value="selectedRegion"
:regions="regions" :regions="regions"
......
import * as awsServices from '../services/aws_services_facade';
import * as types from './mutation_types'; import * as types from './mutation_types';
export const requestRegions = ({ commit }) => commit(types.REQUEST_REGIONS);
export const receiveRegionsSuccess = ({ commit }, payload) => {
commit(types.RECEIVE_REGIONS_SUCCESS, payload);
};
export const receiveRegionsError = ({ commit }, payload) => {
commit(types.RECEIVE_REGIONS_ERROR, payload);
};
export const fetchRegions = ({ dispatch }) => {
dispatch('requestRegions');
return awsServices
.fetchRegions()
.then(regions => dispatch('receiveRegionsSuccess', { regions }))
.catch(error => dispatch('receiveRegionsError', { error }));
};
export const setRegion = ({ commit }, payload) => { export const setRegion = ({ commit }, payload) => {
commit(types.SET_REGION, payload); commit(types.SET_REGION, payload);
}; };
......
import * as types from './mutation_types';
export default fetchItems => ({
requestItems: ({ commit }) => commit(types.REQUEST_ITEMS),
receiveItemsSuccess: ({ commit }, payload) => commit(types.RECEIVE_ITEMS_SUCCESS, payload),
receiveItemsError: ({ commit }, payload) => commit(types.RECEIVE_ITEMS_ERROR, payload),
fetchItems: ({ dispatch }) => {
dispatch('requestItems');
return fetchItems()
.then(items => dispatch('receiveItemsSuccess', { items }))
.catch(error => dispatch('receiveItemsError', { error }));
},
});
import * as getters from './getters';
import actions from './actions';
import mutations from './mutations';
import state from './state';
const createStore = fetchFn => ({
actions: actions(fetchFn),
getters,
mutations,
state: state(),
});
export default createStore;
export const REQUEST_ITEMS = 'REQUEST_ITEMS';
export const RECEIVE_ITEMS_SUCCESS = 'REQUEST_ITEMS_SUCCESS';
export const RECEIVE_ITEMS_ERROR = 'RECEIVE_ITEMS_ERROR';
import * as types from './mutation_types';
export default {
[types.REQUEST_ITEMS](state) {
state.isLoadingItems = true;
state.loadingItemsError = null;
},
[types.RECEIVE_ITEMS_SUCCESS](state, { items }) {
state.isLoadingItems = false;
state.items = items;
},
[types.RECEIVE_ITEMS_ERROR](state, { error }) {
state.isLoadingItems = false;
state.loadingItemsError = error;
},
};
export default () => ({
isLoadingItems: false,
items: [],
loadingItemsError: null,
});
...@@ -4,12 +4,22 @@ import * as getters from './getters'; ...@@ -4,12 +4,22 @@ import * as getters from './getters';
import mutations from './mutations'; import mutations from './mutations';
import state from './state'; import state from './state';
import clusterDropdownStore from './cluster_dropdown';
import * as awsServices from '../services/aws_services_facade';
const createStore = () => const createStore = () =>
new Vuex.Store({ new Vuex.Store({
actions, actions,
getters, getters,
mutations, mutations,
state: state(), state: state(),
modules: {
regions: {
namespaced: true,
...clusterDropdownStore(awsServices.fetchRegions),
},
},
}); });
export default createStore; export default createStore;
export const REQUEST_REGIONS = 'REQUEST_REGIONS'; // eslint-disable-next-line import/prefer-default-export
export const RECEIVE_REGIONS_SUCCESS = 'REQUEST_REGIONS_SUCCESS';
export const RECEIVE_REGIONS_ERROR = 'RECEIVE_REGIONS_ERROR';
export const SET_REGION = 'SET_REGION'; export const SET_REGION = 'SET_REGION';
import * as types from './mutation_types'; import * as types from './mutation_types';
export default { export default {
[types.REQUEST_REGIONS](state) {
state.isLoadingRegions = true;
state.loadingRegionsError = null;
},
[types.RECEIVE_REGIONS_SUCCESS](state, { regions }) {
state.isLoadingRegions = false;
state.regions = regions;
},
[types.RECEIVE_REGIONS_ERROR](state, { error }) {
state.isLoadingRegions = false;
state.loadingRegionsError = error;
},
[types.SET_REGION](state, { region }) { [types.SET_REGION](state, { region }) {
state.selectedRegion = region; state.selectedRegion = region;
}, },
......
...@@ -2,27 +2,9 @@ export default () => ({ ...@@ -2,27 +2,9 @@ export default () => ({
isValidatingCredentials: false, isValidatingCredentials: false,
validCredentials: false, validCredentials: false,
isLoadingRegions: false,
isLoadingRoles: false,
isLoadingVPCs: false,
isLoadingSubnets: false,
isLoadingSecurityGroups: false,
regions: [],
roles: [],
vpcs: [],
subnets: [],
securityGroups: [],
loadingRegionsError: null,
loadingRolesError: null,
loadingVPCsError: null,
loadingSubnetsError: null,
loadingSecurityGroupsError: null,
selectedRegion: '', selectedRegion: '',
selectedRole: '', selectedRole: '',
selectedVPC: '', selectedVpc: '',
selectedSubnet: '', selectedSubnet: '',
selectedSecurityGroup: '', selectedSecurityGroup: '',
}); });
...@@ -4,28 +4,38 @@ import Vue from 'vue'; ...@@ -4,28 +4,38 @@ import Vue from 'vue';
import EksClusterConfigurationForm from '~/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue'; import EksClusterConfigurationForm from '~/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue';
import RegionDropdown from '~/create_cluster/eks_cluster/components/region_dropdown.vue'; import RegionDropdown from '~/create_cluster/eks_cluster/components/region_dropdown.vue';
import clusterDropdownStoreState from '~/create_cluster/eks_cluster/store/cluster_dropdown/state';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
describe('EksClusterConfigurationForm', () => { describe('EksClusterConfigurationForm', () => {
let store; let store;
let actions; let actions;
let state; let regionsState;
let regionsActions;
let vm; let vm;
beforeEach(() => { beforeEach(() => {
actions = { actions = {
fetchRegions: jest.fn(),
setRegion: jest.fn(), setRegion: jest.fn(),
setVpc: jest.fn(),
};
regionsActions = {
fetchItems: jest.fn(),
}; };
state = { regionsState = {
regions: [{ name: 'region 1' }], ...clusterDropdownStoreState(),
isLoadingRegions: false,
loadingRegionsError: { message: '' },
}; };
store = new Vuex.Store({ store = new Vuex.Store({
state,
actions, actions,
modules: {
regions: {
namespaced: true,
state: regionsState,
actions: regionsActions,
},
},
}); });
}); });
...@@ -44,31 +54,35 @@ describe('EksClusterConfigurationForm', () => { ...@@ -44,31 +54,35 @@ describe('EksClusterConfigurationForm', () => {
describe('when mounted', () => { describe('when mounted', () => {
it('fetches available regions', () => { it('fetches available regions', () => {
expect(actions.fetchRegions).toHaveBeenCalled(); expect(regionsActions.fetchItems).toHaveBeenCalled();
}); });
}); });
it('sets isLoadingRegions to RegionDropdown loading property', () => { it('sets isLoadingRegions to RegionDropdown loading property', () => {
state.isLoadingRegions = true; regionsState.isLoadingItems = true;
return Vue.nextTick().then(() => { return Vue.nextTick().then(() => {
expect(findRegionDropdown().props('loading')).toEqual(state.isLoadingRegions); expect(findRegionDropdown().props('loading')).toEqual(regionsState.isLoadingItems);
}); });
}); });
it('sets regions to RegionDropdown regions property', () => { it('sets regions to RegionDropdown regions property', () => {
expect(findRegionDropdown().props('regions')).toEqual(state.regions); expect(findRegionDropdown().props('regions')).toEqual(regionsState.items);
}); });
it('sets loadingRegionsError to RegionDropdown error property', () => { it('sets loadingRegionsError to RegionDropdown error property', () => {
expect(findRegionDropdown().props('error')).toEqual(state.loadingRegionsError); expect(findRegionDropdown().props('error')).toEqual(regionsState.loadingItemsError);
}); });
it('dispatches setRegion action when region is selected', () => { describe('when region is selected', () => {
const region = { region: 'us-west-2' }; const region = { name: 'us-west-2' };
findRegionDropdown().vm.$emit('input', region); beforeEach(() => {
findRegionDropdown().vm.$emit('input', region);
});
expect(actions.setRegion).toHaveBeenCalledWith(expect.anything(), { region }, undefined); it('dispatches setRegion action', () => {
expect(actions.setRegion).toHaveBeenCalledWith(expect.anything(), { region }, undefined);
});
}); });
}); });
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import * as awsServicesFacade from '~/create_cluster/eks_cluster/services/aws_services_facade';
import createState from '~/create_cluster/eks_cluster/store/state'; import createState from '~/create_cluster/eks_cluster/store/state';
import * as types from '~/create_cluster/eks_cluster/store/mutation_types'; import * as types from '~/create_cluster/eks_cluster/store/mutation_types';
import * as actions from '~/create_cluster/eks_cluster/store/actions'; import * as actions from '~/create_cluster/eks_cluster/store/actions';
describe('EKS Cluster Store Actions', () => { describe('EKS Cluster Store Actions', () => {
const regions = [{ name: 'region 1' }];
describe('fetchRegions', () => {
describe('on success', () => {
beforeEach(() => {
jest.spyOn(awsServicesFacade, 'fetchRegions').mockResolvedValueOnce(regions);
});
it('dispatches success with received regions', () =>
testAction(
actions.fetchRegions,
null,
createState(),
[],
[
{ type: 'requestRegions' },
{
type: 'receiveRegionsSuccess',
payload: { regions },
},
],
));
});
describe('on failure', () => {
const error = new Error('Could not fetch regions');
beforeEach(() => {
jest.spyOn(awsServicesFacade, 'fetchRegions').mockRejectedValueOnce(error);
});
it('dispatches success with received regions', () =>
testAction(
actions.fetchRegions,
null,
createState(),
[],
[
{ type: 'requestRegions' },
{
type: 'receiveRegionsError',
payload: { error },
},
],
));
});
});
describe('requestRegions', () => {
it(`commits ${types.REQUEST_REGIONS} mutation`, () =>
testAction(actions.requestRegions, null, createState(), [{ type: types.REQUEST_REGIONS }]));
});
describe('receiveRegionsSuccess', () => {
it(`commits ${types.RECEIVE_REGIONS_SUCCESS} mutation`, () =>
testAction(actions.receiveRegionsSuccess, { regions }, createState(), [
{
type: types.RECEIVE_REGIONS_SUCCESS,
payload: {
regions,
},
},
]));
});
describe('receiveRegionsError', () => {
it(`commits ${types.RECEIVE_REGIONS_ERROR} mutation`, () => {
const error = new Error('Error fetching regions');
testAction(actions.receiveRegionsError, { error }, createState(), [
{
type: types.RECEIVE_REGIONS_ERROR,
payload: {
error,
},
},
]);
});
});
describe('setRegion', () => { describe('setRegion', () => {
it(`commits ${types.SET_REGION} mutation`, () => { it(`commits ${types.SET_REGION} mutation`, () => {
const region = { name: 'west-1' }; const region = { name: 'west-1' };
......
import testAction from 'helpers/vuex_action_helper';
import createState from '~/create_cluster/eks_cluster/store/cluster_dropdown/state';
import * as types from '~/create_cluster/eks_cluster/store/cluster_dropdown/mutation_types';
import actionsFactory from '~/create_cluster/eks_cluster/store/cluster_dropdown/actions';
describe('Cluster dropdown Store Actions', () => {
const items = [{ name: 'item 1' }];
let fetchFn;
let actions;
beforeEach(() => {
fetchFn = jest.fn();
actions = actionsFactory(fetchFn);
});
describe('fetchItems', () => {
describe('on success', () => {
beforeEach(() => {
fetchFn.mockResolvedValueOnce(items);
actions = actionsFactory(fetchFn);
});
it('dispatches success with received items', () =>
testAction(
actions.fetchItems,
null,
createState(),
[],
[
{ type: 'requestItems' },
{
type: 'receiveItemsSuccess',
payload: { items },
},
],
));
});
describe('on failure', () => {
const error = new Error('Could not fetch items');
beforeEach(() => {
fetchFn.mockRejectedValueOnce(error);
});
it('dispatches success with received items', () =>
testAction(
actions.fetchItems,
null,
createState(),
[],
[
{ type: 'requestItems' },
{
type: 'receiveItemsError',
payload: { error },
},
],
));
});
});
describe('requestItems', () => {
it(`commits ${types.REQUEST_ITEMS} mutation`, () =>
testAction(actions.requestItems, null, createState(), [{ type: types.REQUEST_ITEMS }]));
});
describe('receiveItemsSuccess', () => {
it(`commits ${types.RECEIVE_ITEMS_SUCCESS} mutation`, () =>
testAction(actions.receiveItemsSuccess, { items }, createState(), [
{
type: types.RECEIVE_ITEMS_SUCCESS,
payload: {
items,
},
},
]));
});
describe('receiveItemsError', () => {
it(`commits ${types.RECEIVE_ITEMS_ERROR} mutation`, () => {
const error = new Error('Error fetching items');
testAction(actions.receiveItemsError, { error }, createState(), [
{
type: types.RECEIVE_ITEMS_ERROR,
payload: {
error,
},
},
]);
});
});
});
import {
REQUEST_ITEMS,
RECEIVE_ITEMS_SUCCESS,
RECEIVE_ITEMS_ERROR,
} from '~/create_cluster/eks_cluster/store/cluster_dropdown/mutation_types';
import createState from '~/create_cluster/eks_cluster/store/cluster_dropdown/state';
import mutations from '~/create_cluster/eks_cluster/store/cluster_dropdown/mutations';
describe('Cluster dropdown store mutations', () => {
let state;
let emptyPayload;
let items;
let error;
beforeEach(() => {
emptyPayload = {};
items = [{ name: 'item 1' }];
error = new Error('could not load error');
state = createState();
});
it.each`
mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
${REQUEST_ITEMS} | ${'isLoadingItems'} | ${emptyPayload} | ${true} | ${true}
${REQUEST_ITEMS} | ${'loadingItemsError'} | ${emptyPayload} | ${null} | ${null}
${RECEIVE_ITEMS_SUCCESS} | ${'isLoadingItems'} | ${{ items }} | ${false} | ${false}
${RECEIVE_ITEMS_SUCCESS} | ${'items'} | ${{ items }} | ${items} | ${'items payload'}
${RECEIVE_ITEMS_ERROR} | ${'isLoadingItems'} | ${{ error }} | ${false} | ${false}
${RECEIVE_ITEMS_ERROR} | ${'error'} | ${{ error }} | ${error} | ${'received error object'}
`(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => {
const { mutation, mutatedProperty, payload, expectedValue } = data;
mutations[mutation](state, payload);
expect(state[mutatedProperty]).toBe(expectedValue);
});
});
import { import { SET_REGION } from '~/create_cluster/eks_cluster/store/mutation_types';
REQUEST_REGIONS,
RECEIVE_REGIONS_ERROR,
RECEIVE_REGIONS_SUCCESS,
SET_REGION,
} from '~/create_cluster/eks_cluster/store/mutation_types';
import createState from '~/create_cluster/eks_cluster/store/state'; import createState from '~/create_cluster/eks_cluster/store/state';
import mutations from '~/create_cluster/eks_cluster/store/mutations'; import mutations from '~/create_cluster/eks_cluster/store/mutations';
describe('Create EKS cluster store mutations', () => { describe('Create EKS cluster store mutations', () => {
let state; let state;
let emptyPayload;
let regions;
let region; let region;
let error;
beforeEach(() => { beforeEach(() => {
emptyPayload = {};
region = { name: 'regions-1' }; region = { name: 'regions-1' };
regions = [region];
error = new Error('could not load error');
state = createState(); state = createState();
}); });
it.each` it.each`
mutation | mutatedProperty | payload | expectedValue | expectedValueDescription mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
${REQUEST_REGIONS} | ${'isLoadingRegions'} | ${emptyPayload} | ${true} | ${true} ${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'}
${REQUEST_REGIONS} | ${'loadingRegionsError'} | ${emptyPayload} | ${null} | ${null}
${RECEIVE_REGIONS_SUCCESS} | ${'isLoadingRegions'} | ${{ regions }} | ${false} | ${false}
${RECEIVE_REGIONS_SUCCESS} | ${'regions'} | ${{ regions }} | ${regions} | ${'regions payload'}
${RECEIVE_REGIONS_ERROR} | ${'isLoadingRegions'} | ${{ error }} | ${false} | ${false}
${RECEIVE_REGIONS_ERROR} | ${'error'} | ${{ error }} | ${error} | ${'received error object'}
${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'}
`(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => { `(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => {
const { mutation, mutatedProperty, payload, expectedValue } = data; const { mutation, mutatedProperty, payload, expectedValue } = data;
......
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