Commit 20005cdf authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch '216134_03-add-geo-settings-api' into 'master'

Geo Settings Form - Fetch Data from API

See merge request gitlab-org/gitlab!35181
parents 4d3ef0f8 d239af20
......@@ -39,6 +39,7 @@ export default {
vulnerabilityActionPath: '/api/:version/vulnerabilities/:id/:action',
featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists',
featureFlagUserList: '/api/:version/projects/:id/feature_flags_user_lists/:list_iid',
applicationSettingsPath: '/api/:version/application/settings',
userSubscription(namespaceId) {
const url = Api.buildUrl(this.subscriptionPath).replace(':id', encodeURIComponent(namespaceId));
......@@ -349,4 +350,9 @@ export default {
return axios.delete(url);
},
getApplicationSettings() {
const url = Api.buildUrl(this.applicationSettingsPath);
return axios.get(url);
},
};
<script>
import { mapActions, mapState } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import GeoSettingsForm from './geo_settings_form.vue';
export default {
name: 'GeoSettingsApp',
components: {
GlLoadingIcon,
GeoSettingsForm,
},
computed: {
...mapState(['isLoading']),
},
created() {
this.fetchGeoSettings();
},
methods: {
...mapActions(['fetchGeoSettings']),
},
};
</script>
......@@ -19,6 +31,7 @@ export default {
)
}}
</p>
<geo-settings-form />
<gl-loading-icon v-if="isLoading" size="xl" />
<geo-settings-form v-else />
</article>
</template>
<script>
import { mapState } from 'vuex';
import { GlFormGroup, GlFormInput, GlButton } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import { DEFAULT_TIMEOUT, DEFAULT_ALLOWED_IP } from '../constants';
export default {
name: 'GeoSettingsForm',
......@@ -10,11 +10,10 @@ export default {
GlFormInput,
GlButton,
},
data() {
return {
timeout: DEFAULT_TIMEOUT,
allowedIp: DEFAULT_ALLOWED_IP,
};
computed: {
// The real connection between vuex and the component will be implemented in
// a later MR, this feature is anyhow behind feature flag
...mapState(['timeout', 'allowedIp']),
},
methods: {
redirect() {
......
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import createStore from './store';
import GeoSettingsApp from './components/app.vue';
Vue.use(Translate);
......@@ -9,6 +10,7 @@ export default () => {
return new Vue({
el,
store: createStore(),
components: {
GeoSettingsApp,
},
......
import Api from 'ee/api';
import createFlash from '~/flash';
import { __ } from '~/locale';
import * as types from './mutation_types';
// eslint-disable-next-line import/prefer-default-export
export const fetchGeoSettings = ({ commit }) => {
commit(types.REQUEST_GEO_SETTINGS);
Api.getApplicationSettings()
.then(({ data }) => {
commit(types.RECEIVE_GEO_SETTINGS_SUCCESS, {
timeout: data.geo_status_timeout,
allowedIp: data.geo_node_allowed_ips,
});
})
.catch(() => {
createFlash(__('There was an error fetching the Geo Settings'));
commit(types.RECEIVE_GEO_SETTINGS_ERROR);
});
};
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import mutations from './mutations';
import createState from './state';
Vue.use(Vuex);
export default () =>
new Vuex.Store({
actions,
mutations,
state: createState(),
});
export const REQUEST_GEO_SETTINGS = 'REQUEST_GEO_SETTINGS';
export const RECEIVE_GEO_SETTINGS_SUCCESS = 'RECEIVE_GEO_SETTINGS_SUCCESS';
export const RECEIVE_GEO_SETTINGS_ERROR = 'RECEIVE_GEO_SETTINGS_ERROR';
import * as types from './mutation_types';
import { DEFAULT_TIMEOUT, DEFAULT_ALLOWED_IP } from '../constants';
export default {
[types.REQUEST_GEO_SETTINGS](state) {
state.isLoading = true;
},
[types.RECEIVE_GEO_SETTINGS_SUCCESS](state, { timeout, allowedIp }) {
state.isLoading = false;
state.timeout = timeout;
state.allowedIp = allowedIp;
},
[types.RECEIVE_GEO_SETTINGS_ERROR](state) {
state.isLoading = false;
state.timeout = DEFAULT_TIMEOUT;
state.allowedIp = DEFAULT_ALLOWED_IP;
},
};
import { DEFAULT_TIMEOUT, DEFAULT_ALLOWED_IP } from '../constants';
export default () => ({
isLoading: false,
timeout: DEFAULT_TIMEOUT,
allowedIp: DEFAULT_ALLOWED_IP,
});
......@@ -841,4 +841,20 @@ describe('Api', () => {
});
});
});
describe('getApplicationSettings', () => {
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/application/settings`;
const apiResponse = { mock_setting: 1, mock_setting2: 2 };
it('fetches applications settings', () => {
jest.spyOn(Api, 'buildUrl').mockReturnValue(expectedUrl);
jest.spyOn(axios, 'get');
mock.onGet(expectedUrl).replyOnce(200, apiResponse);
return Api.getApplicationSettings().then(({ data }) => {
expect(data).toEqual(apiResponse);
expect(axios.get).toHaveBeenCalledWith(expectedUrl);
});
});
});
});
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui';
import initStore from 'ee/geo_settings/store';
import * as types from 'ee/geo_settings/store/mutation_types';
import GeoSettingsApp from 'ee/geo_settings/components/app.vue';
import GeoSettingsForm from 'ee/geo_settings/components/geo_settings_form.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('GeoSettingsApp', () => {
let wrapper;
let store;
const createStore = () => {
store = initStore();
jest.spyOn(store, 'dispatch').mockImplementation();
};
const createComponent = () => {
wrapper = shallowMount(GeoSettingsApp);
wrapper = shallowMount(GeoSettingsApp, {
store,
});
};
afterEach(() => {
......@@ -14,10 +31,12 @@ describe('GeoSettingsApp', () => {
});
const findGeoSettingsContainer = () => wrapper.find('[data-testid="geoSettingsContainer"]');
const findGeoSettingsForm = () => wrapper.find(GeoSettingsForm);
const containsGeoSettingsForm = () => wrapper.contains(GeoSettingsForm);
const containsGlLoadingIcon = () => wrapper.contains(GlLoadingIcon);
describe('renders', () => {
beforeEach(() => {
createStore();
createComponent();
});
......@@ -29,8 +48,39 @@ describe('GeoSettingsApp', () => {
expect(findGeoSettingsContainer().text()).toContain('Geo Settings');
});
it('Geo Settings Form', () => {
expect(findGeoSettingsForm().exists()).toBe(true);
describe('when not loading', () => {
it('Geo Settings Form', () => {
expect(containsGeoSettingsForm()).toBe(true);
});
it('not GlLoadingIcon', () => {
expect(containsGlLoadingIcon()).toBe(false);
});
});
describe('when loading', () => {
beforeEach(() => {
store.commit(types.REQUEST_GEO_SETTINGS);
});
it('not Geo Settings Form', () => {
expect(containsGeoSettingsForm()).toBe(false);
});
it('GlLoadingIcon', () => {
expect(containsGlLoadingIcon()).toBe(true);
});
});
});
describe('onCreate', () => {
beforeEach(() => {
createStore();
createComponent();
});
it('calls fetchGeoSettings', () => {
expect(store.dispatch).toHaveBeenCalledWith('fetchGeoSettings');
});
});
});
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { visitUrl } from '~/lib/utils/url_utility';
import store from 'ee/geo_settings/store';
import GeoSettingsForm from 'ee/geo_settings/components/geo_settings_form.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn().mockName('visitUrlMock'),
}));
......@@ -10,7 +15,9 @@ describe('GeoSettingsForm', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(GeoSettingsForm);
wrapper = shallowMount(GeoSettingsForm, {
store,
});
};
afterEach(() => {
......
export const MOCK_APPLICATION_SETTINGS_FETCH_RESPONSE = {
geo_status_timeout: 10,
geo_node_allowed_ips: '0.0.0.0/0, ::/0',
};
export const MOCK_BASIC_SETTINGS_DATA = {
timeout: MOCK_APPLICATION_SETTINGS_FETCH_RESPONSE.geo_status_timeout,
allowedIp: MOCK_APPLICATION_SETTINGS_FETCH_RESPONSE.geo_node_allowed_ips,
};
import testAction from 'helpers/vuex_action_helper';
import flash from '~/flash';
import Api from 'ee/api';
import * as actions from 'ee/geo_settings/store/actions';
import * as types from 'ee/geo_settings/store/mutation_types';
import state from 'ee/geo_settings/store/state';
import { MOCK_BASIC_SETTINGS_DATA, MOCK_APPLICATION_SETTINGS_FETCH_RESPONSE } from '../mock_data';
jest.mock('~/flash');
describe('GeoSettings Store Actions', () => {
describe('fetchGeoSettings', () => {
describe('on success', () => {
beforeEach(() => {
jest
.spyOn(Api, 'getApplicationSettings')
.mockResolvedValue({ data: MOCK_APPLICATION_SETTINGS_FETCH_RESPONSE });
});
it('should commit the request and success actions', done => {
testAction(
actions.fetchGeoSettings,
{},
state,
[
{ type: types.REQUEST_GEO_SETTINGS },
{ type: types.RECEIVE_GEO_SETTINGS_SUCCESS, payload: MOCK_BASIC_SETTINGS_DATA },
],
[],
done,
);
});
});
describe('on error', () => {
beforeEach(() => {
jest.spyOn(Api, 'getApplicationSettings').mockRejectedValue(new Error(500));
});
it('should commit the request and error actions', () => {
testAction(
actions.fetchGeoSettings,
{},
state,
[{ type: types.REQUEST_GEO_SETTINGS }, { type: types.RECEIVE_GEO_SETTINGS_ERROR }],
[],
() => {
expect(flash).toHaveBeenCalledTimes(1);
},
);
});
});
});
});
import mutations from 'ee/geo_settings/store/mutations';
import createState from 'ee/geo_settings/store/state';
import * as types from 'ee/geo_settings/store/mutation_types';
import { DEFAULT_TIMEOUT, DEFAULT_ALLOWED_IP } from 'ee/geo_settings/constants';
import { MOCK_BASIC_SETTINGS_DATA } from '../mock_data';
describe('GeoSettings Store Mutations', () => {
let state;
beforeEach(() => {
state = createState();
});
describe('REQUEST_GEO_SETTINGS', () => {
it('sets isLoading to true', () => {
mutations[types.REQUEST_GEO_SETTINGS](state);
expect(state.isLoading).toEqual(true);
});
});
describe('RECEIVE_GEO_SETTINGS_SUCCESS', () => {
const mockData = MOCK_BASIC_SETTINGS_DATA;
beforeEach(() => {
state.isLoading = true;
});
it('sets isLoading to false', () => {
mutations[types.RECEIVE_GEO_SETTINGS_SUCCESS](state, mockData);
expect(state.isLoading).toEqual(false);
});
it('sets timeout and allowedIp array with data', () => {
mutations[types.RECEIVE_GEO_SETTINGS_SUCCESS](state, mockData);
expect(state.timeout).toBe(mockData.timeout);
expect(state.allowedIp).toBe(mockData.allowedIp);
});
});
describe('RECEIVE_GEO_SETTINGS_ERROR', () => {
const mockData = MOCK_BASIC_SETTINGS_DATA;
beforeEach(() => {
state.isLoading = true;
state.timeout = mockData.timeout;
state.allowedIp = mockData.allowedIp;
});
it('sets isLoading to false', () => {
mutations[types.RECEIVE_GEO_SETTINGS_ERROR](state);
expect(state.isLoading).toEqual(false);
});
it('resets timeout and allowedIp array', () => {
mutations[types.RECEIVE_GEO_SETTINGS_ERROR](state);
expect(state.timeout).toBe(DEFAULT_TIMEOUT);
expect(state.allowedIp).toBe(DEFAULT_ALLOWED_IP);
});
});
});
......@@ -22954,6 +22954,9 @@ msgstr ""
msgid "There was an error fetching the %{replicableType}"
msgstr ""
msgid "There was an error fetching the Geo Settings"
msgstr ""
msgid "There was an error fetching the Node's Groups"
msgstr ""
......
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