Commit 6d31d55b authored by Peter Hegman's avatar Peter Hegman Committed by Brandon Labuschagne

Setup LDAP override confirmation modal

Part of a larger initiative to convert group members view from HAML
to Vue
parent 390b1abc
......@@ -27,6 +27,10 @@ export default {
RoleDropdown,
RemoveGroupLinkModal,
ExpirationDatepicker,
LdapOverrideConfirmationModal: () =>
import(
'ee_component/vue_shared/components/members/ldap/ldap_override_confirmation_modal.vue'
),
},
computed: {
...mapState(['members', 'tableFields']),
......@@ -114,5 +118,6 @@ export default {
</template>
</gl-table>
<remove-group-link-modal />
<ldap-override-confirmation-modal />
</div>
</template>
import createState from 'ee_else_ce/vuex_shared/modules/members/state';
import * as actions from './actions';
import mutations from './mutations';
import mutations from 'ee_else_ce/vuex_shared/modules/members/mutations';
import * as actions from 'ee_else_ce/vuex_shared/modules/members/actions';
export default initialState => ({
namespaced: true,
......
export const LDAP_OVERRIDE_CONFIRMATION_MODAL_ID = 'ldap-override-confirmation-modal';
<script>
import { mapState, mapActions } from 'vuex';
import { GlModal, GlSprintf } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { LDAP_OVERRIDE_CONFIRMATION_MODAL_ID } from '../constants';
export default {
name: 'LdapOverrideConfirmationModal',
i18n: {
editPermissions: s__('Members|Edit permissions'),
modalBody: s__(
'Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync.',
),
toastMessage: s__('Members|LDAP override enabled.'),
},
actionCancel: {
text: __('Cancel'),
},
modalId: LDAP_OVERRIDE_CONFIRMATION_MODAL_ID,
components: { GlModal, GlSprintf },
data() {
return {
busy: false,
};
},
computed: {
...mapState(['memberToOverride', 'ldapOverrideConfirmationModalVisible']),
actionPrimary() {
return {
text: this.$options.i18n.editPermissions,
attributes: {
variant: 'warning',
loading: this.busy,
},
};
},
},
methods: {
...mapActions(['updateLdapOverride', 'hideLdapOverrideConfirmationModal']),
handlePrimary() {
this.busy = true;
this.updateLdapOverride({ memberId: this.memberToOverride.id, override: true })
.then(() => {
this.busy = false;
this.hideLdapOverrideConfirmationModal();
this.$toast.show(this.$options.i18n.toastMessage);
})
.catch(() => {
this.hideLdapOverrideConfirmationModal();
this.busy = false;
});
},
},
};
</script>
<template>
<gl-modal
v-bind="$attrs"
:modal-id="$options.modalId"
:title="$options.i18n.editPermissions"
:visible="ldapOverrideConfirmationModalVisible"
:action-primary="actionPrimary"
:action-cancel="$options.actionCancel"
size="sm"
@primary="handlePrimary"
@hide="hideLdapOverrideConfirmationModal"
>
<p v-if="memberToOverride">
<gl-sprintf :message="$options.i18n.modalBody">
<template #userName>{{ memberToOverride.user.name }}</template>
</gl-sprintf>
</p>
</gl-modal>
</template>
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
export * from '~/vuex_shared/modules/members/actions';
export const updateLdapOverride = async ({ state, commit }, { memberId, override }) => {
try {
await axios.patch(
state.ldapOverridePath.replace(':id', memberId),
state.requestFormatter({ override }),
);
commit(types.RECEIVE_LDAP_OVERRIDE_SUCCESS, {
memberId,
override,
});
} catch (error) {
commit(types.RECEIVE_LDAP_OVERRIDE_ERROR, override);
throw error;
}
};
export const showLdapOverrideConfirmationModal = ({ commit }, member) => {
commit(types.SHOW_LDAP_OVERRIDE_CONFIRMATION_MODAL, member);
};
export const hideLdapOverrideConfirmationModal = ({ commit }) => {
commit(types.HIDE_LDAP_OVERRIDE_CONFIRMATION_MODAL);
};
export const RECEIVE_LDAP_OVERRIDE_SUCCESS = 'RECEIVE_LDAP_OVERRIDE_SUCCESS';
export const RECEIVE_LDAP_OVERRIDE_ERROR = 'RECEIVE_LDAP_OVERRIDE_ERROR';
export const SHOW_LDAP_OVERRIDE_CONFIRMATION_MODAL = 'SHOW_LDAP_OVERRIDE_CONFIRMATION_MODAL';
export const HIDE_LDAP_OVERRIDE_CONFIRMATION_MODAL = 'HIDE_LDAP_OVERRIDE_CONFIRMATION_MODAL';
import Vue from 'vue';
import * as types from './mutation_types';
import CEMutations from '~/vuex_shared/modules/members/mutations';
import { s__ } from '~/locale';
import { findMember } from '~/vuex_shared/modules/members/utils';
export default {
...CEMutations,
[types.RECEIVE_LDAP_OVERRIDE_SUCCESS](state, { memberId, override }) {
const member = findMember(state, memberId);
if (!member) {
return;
}
Vue.set(member, 'isOverridden', override);
},
[types.RECEIVE_LDAP_OVERRIDE_ERROR](state, override) {
state.errorMessage = override
? s__('Members|An error occurred while trying to enable LDAP override, please try again.')
: s__(
'Members|An error occurred while trying to revert to LDAP group sync settings, please try again.',
);
state.showError = true;
},
[types.SHOW_LDAP_OVERRIDE_CONFIRMATION_MODAL](state, member) {
state.ldapOverrideConfirmationModalVisible = true;
state.memberToOverride = member;
},
[types.HIDE_LDAP_OVERRIDE_CONFIRMATION_MODAL](state) {
state.ldapOverrideConfirmationModalVisible = false;
},
};
......@@ -5,6 +5,8 @@ export default initialState => {
return {
ldapOverridePath,
memberToOverride: null,
ldapOverrideConfirmationModalVisible: false,
...createState(initialState),
};
};
import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui';
import { nextTick } from 'vue';
import { within } from '@testing-library/dom';
import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import { member } from 'jest/vue_shared/components/members/mock_data';
import { LDAP_OVERRIDE_CONFIRMATION_MODAL_ID } from 'ee/vue_shared/components/members/constants';
import LdapOverrideConfirmationModal from 'ee/vue_shared/components/members/ldap/ldap_override_confirmation_modal.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('LdapOverrideConfirmationModal', () => {
let wrapper;
let resolveUpdateLdapOverride;
let actions;
const $toast = {
show: jest.fn(),
};
const createStore = (state = {}) => {
actions = {
updateLdapOverride: jest.fn(
() =>
new Promise(resolve => {
resolveUpdateLdapOverride = resolve;
}),
),
hideLdapOverrideConfirmationModal: jest.fn(),
};
return new Vuex.Store({
state: {
memberToOverride: member,
ldapOverrideConfirmationModalVisible: true,
...state,
},
actions,
});
};
const createComponent = state => {
wrapper = mount(LdapOverrideConfirmationModal, {
localVue,
store: createStore(state),
attrs: {
static: true,
},
mocks: {
$toast,
},
});
};
const findModal = () => wrapper.find(GlModal);
const getByText = (text, options) =>
createWrapper(within(findModal().element).getByText(text, options));
const getEditPermissionsButton = () =>
getByText('Edit permissions', { selector: 'button > span' });
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('when modal is open', () => {
beforeEach(async () => {
createComponent();
await nextTick();
});
it('sets modal ID', () => {
expect(findModal().props('modalId')).toBe(LDAP_OVERRIDE_CONFIRMATION_MODAL_ID);
});
it('displays modal title', () => {
expect(getByText('Edit permissions', { selector: 'h4' }).exists()).toBe(true);
});
it('displays modal body', () => {
expect(
getByText(
`${member.user.name} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync.`,
).exists(),
).toBe(true);
});
it('calls `hideLdapOverrideConfirmationModal` action when modal is closed', () => {
getByText('Cancel').trigger('click');
expect(actions.hideLdapOverrideConfirmationModal).toHaveBeenCalled();
});
describe('When "Edit permissions" button is clicked', () => {
beforeEach(async () => {
getEditPermissionsButton().trigger('click');
});
it('calls `updateLdapOverride` Vuex action', () => {
expect(actions.updateLdapOverride).toHaveBeenCalledWith(expect.any(Object), {
memberId: member.id,
override: true,
});
});
it('displays toast when successful', async () => {
resolveUpdateLdapOverride();
await waitForPromises();
expect($toast.show).toHaveBeenCalledWith('LDAP override enabled.');
});
it('sets primary button to loading state while waiting for `updateLdapOverride` to resolve', async () => {
expect(getEditPermissionsButton().element.closest('button[disabled="disabled"]')).not.toBe(
null,
);
resolveUpdateLdapOverride();
await waitForPromises();
expect(getEditPermissionsButton().element.closest('button[disabled="disabled"]')).toBe(
null,
);
});
});
});
it('modal does not show when `ldapOverrideConfirmationModalVisible` is `false`', () => {
createComponent({ ldapOverrideConfirmationModalVisible: false });
expect(findModal().vm.$attrs.visible).toBe(false);
});
});
import { noop } from 'lodash';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { members, member } from 'jest/vue_shared/components/members/mock_data';
import testAction from 'helpers/vuex_action_helper';
import * as types from 'ee/vuex_shared/modules/members/mutation_types';
import {
updateLdapOverride,
showLdapOverrideConfirmationModal,
hideLdapOverrideConfirmationModal,
} from 'ee/vuex_shared/modules/members/actions';
import httpStatusCodes from '~/lib/utils/http_status';
describe('Vuex members actions', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('updateLdapOverride', () => {
const payload = {
memberId: members[0].id,
override: true,
};
const state = {
members,
ldapOverridePath: '/groups/ldap-group/-/group_members/:id/override',
requestFormatter: noop,
};
describe('successful request', () => {
it(`commits ${types.RECEIVE_LDAP_OVERRIDE_SUCCESS} mutation`, async () => {
mock.onPatch().replyOnce(httpStatusCodes.OK);
await testAction(updateLdapOverride, payload, state, [
{
type: types.RECEIVE_LDAP_OVERRIDE_SUCCESS,
payload,
},
]);
expect(mock.history.patch[0].url).toBe('/groups/ldap-group/-/group_members/238/override');
});
});
describe('unsuccessful request', () => {
beforeEach(() => {
mock.onPatch().networkError();
});
it(`commits ${types.RECEIVE_LDAP_OVERRIDE_ERROR} mutation and throws error`, async () => {
await expect(
testAction(updateLdapOverride, {}, state, [
{
type: types.RECEIVE_LDAP_OVERRIDE_ERROR,
},
]),
).rejects.toThrowError(new Error('Network Error'));
});
});
});
describe('LDAP override confirmation modal', () => {
const state = {
memberToOverride: null,
ldapOverrideConfirmationModalVisible: false,
};
describe('showLdapOverrideConfirmationModal', () => {
it(`commits ${types.SHOW_LDAP_OVERRIDE_CONFIRMATION_MODAL} mutation`, () => {
testAction(showLdapOverrideConfirmationModal, member, state, [
{
type: types.SHOW_LDAP_OVERRIDE_CONFIRMATION_MODAL,
payload: member,
},
]);
});
});
describe('hideLdapOverrideConfirmationModal', () => {
it(`commits ${types.HIDE_LDAP_OVERRIDE_CONFIRMATION_MODAL} mutation`, () => {
testAction(hideLdapOverrideConfirmationModal, {}, state, [
{
type: types.HIDE_LDAP_OVERRIDE_CONFIRMATION_MODAL,
},
]);
});
});
});
});
import { members, member } from 'jest/vue_shared/components/members/mock_data';
import mutations from 'ee/vuex_shared/modules/members/mutations';
import * as types from 'ee/vuex_shared/modules/members/mutation_types';
describe('Vuex members mutations', () => {
describe(types.RECEIVE_LDAP_OVERRIDE_SUCCESS, () => {
it('updates member', () => {
const state = {
members,
};
mutations[types.RECEIVE_LDAP_OVERRIDE_SUCCESS](state, {
memberId: members[0].id,
override: true,
});
expect(state.members[0].isOverridden).toEqual(true);
});
});
describe(types.RECEIVE_LDAP_OVERRIDE_ERROR, () => {
const state = {
showError: false,
errorMessage: '',
};
describe('when enabling LDAP override', () => {
it('shows error message', () => {
mutations[types.RECEIVE_LDAP_OVERRIDE_ERROR](state, true);
expect(state.showError).toBe(true);
expect(state.errorMessage).toBe(
'An error occurred while trying to enable LDAP override, please try again.',
);
});
});
describe('when disabling LDAP override', () => {
it('shows error message', () => {
mutations[types.RECEIVE_LDAP_OVERRIDE_ERROR](state, false);
expect(state.showError).toBe(true);
expect(state.errorMessage).toBe(
'An error occurred while trying to revert to LDAP group sync settings, please try again.',
);
});
});
});
describe(types.SHOW_LDAP_OVERRIDE_CONFIRMATION_MODAL, () => {
it('sets `ldapOverrideConfirmationModalVisible` and `memberToOverride`', () => {
const state = {
memberToOverride: null,
ldapOverrideConfirmationModalVisible: false,
};
mutations[types.SHOW_LDAP_OVERRIDE_CONFIRMATION_MODAL](state, member);
expect(state).toEqual({
memberToOverride: member,
ldapOverrideConfirmationModalVisible: true,
});
});
});
describe(types.HIDE_LDAP_OVERRIDE_CONFIRMATION_MODAL, () => {
it('sets `ldapOverrideConfirmationModalVisible` to `false`', () => {
const state = {
ldapOverrideConfirmationModalVisible: true,
};
mutations[types.HIDE_LDAP_OVERRIDE_CONFIRMATION_MODAL](state);
expect(state.ldapOverrideConfirmationModalVisible).toBe(false);
});
});
});
......@@ -16256,6 +16256,15 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
msgstr ""
msgid "Members|An error occurred while trying to revert to LDAP group sync settings, please try again."
msgstr ""
msgid "Members|An error occurred while updating the member's expiration date, please try again."
msgstr ""
......@@ -16283,6 +16292,9 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
msgid "Members|Edit permissions"
msgstr ""
msgid "Members|Expiration date removed successfully."
msgstr ""
......@@ -16292,6 +16304,9 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
......
......@@ -3,6 +3,7 @@ export const member = {
canUpdate: false,
canRemove: false,
canOverride: false,
isOverridden: false,
accessLevel: { integerValue: 50, stringValue: 'Owner' },
source: {
id: 178,
......
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