Skip to content
代码片段 群组 项目
提交 27ec5cf5 编辑于 作者: Phil Hughes's avatar Phil Hughes
浏览文件

Merge branch '381683-delete-excluded-namespace' into 'master'

No related branches found
No related tags found
无相关合并请求
<script> <script>
import { GlAlert, GlTable, GlLoadingIcon } from '@gitlab/ui'; import { GlAlert, GlTable, GlLoadingIcon, GlButton, GlModal } from '@gitlab/ui';
import Api from '~/api'; import Api from '~/api';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale'; import { __, sprintf } from '~/locale';
import { import {
LIST_EXCLUSIONS_ENDPOINT, LIST_EXCLUSIONS_ENDPOINT,
DELETE_EXCLUSION_ENDPOINT,
exclusionListFetchError, exclusionListFetchError,
exclusionDeleteError,
excludedNamespacesDescription, excludedNamespacesDescription,
deleteModalBody,
deleteModalTitle,
deleteModalProps,
} from '../constants'; } from '../constants';
import ExcludedNamespacesForm from './excluded_namespaces_form.vue'; import ExcludedNamespacesForm from './excluded_namespaces_form.vue';
export default { export default {
components: { components: {
GlAlert, GlAlert,
GlButton,
GlTable, GlTable,
GlModal,
GlLoadingIcon, GlLoadingIcon,
ExcludedNamespacesForm, ExcludedNamespacesForm,
}, },
...@@ -28,14 +35,14 @@ export default { ...@@ -28,14 +35,14 @@ export default {
'operations', 'operations',
], ],
fetchError: null, fetchError: null,
namespaceIdToBeConfirmed: null,
}; };
}, },
created() { created() {
this.fetchExclusions(); this.fetchExclusions();
}, },
i18n: { i18n: { excludedNamespacesDescription, deleteModalTitle, deleteModalBody },
excludedNamespacesDescription, deleteModalProps,
},
methods: { methods: {
async fetchExclusions() { async fetchExclusions() {
const endpoint = Api.buildUrl(LIST_EXCLUSIONS_ENDPOINT); const endpoint = Api.buildUrl(LIST_EXCLUSIONS_ENDPOINT);
...@@ -52,6 +59,26 @@ export default { ...@@ -52,6 +59,26 @@ export default {
this.loading = false; this.loading = false;
} }
}, },
openConfirmationModal(namespaceId) {
this.namespaceIdToBeConfirmed = namespaceId;
this.$refs.modal.show();
},
handleModalConfirmation() {
this.deleteExclusion(this.namespaceIdToBeConfirmed);
// reset namespaceIdToBeConfirmed to be ready for next usage
this.namespaceIdToBeConfirmed = null;
},
async deleteExclusion(namespaceId) {
const endpoint = Api.buildUrl(DELETE_EXCLUSION_ENDPOINT).replace(':id', namespaceId);
try {
await axios.delete(endpoint);
this.fetchExclusions();
} catch (error) {
const errorMessage = error.response?.data?.message || error.message;
this.fetchError = sprintf(exclusionDeleteError, { errorMessage });
}
},
}, },
}; };
</script> </script>
...@@ -71,7 +98,25 @@ export default { ...@@ -71,7 +98,25 @@ export default {
<gl-loading-icon /> <gl-loading-icon />
</div> </div>
</template> </template>
<template #cell(operations)="{ item }">
<gl-button
category="primary"
variant="danger"
@click="openConfirmationModal(item.namespace_id)"
>{{ __('Delete') }}</gl-button
>
</template>
</gl-table> </gl-table>
<gl-modal
ref="modal"
modal-id="namespace-exclusion-modal"
:title="$options.i18n.deleteModalTitle"
:action-primary="$options.deleteModalProps.primaryProps"
:action-cancel="$options.deleteModalProps.cancelProps"
@primary="handleModalConfirmation"
>
{{ $options.i18n.deleteModalBody }}
</gl-modal>
<br /> <br />
<excluded-namespaces-form @added="fetchExclusions" /> <excluded-namespaces-form @added="fetchExclusions" />
</div> </div>
......
import { s__ } from '~/locale'; import { s__, __ } from '~/locale';
export const LIST_EXCLUSIONS_ENDPOINT = '/api/:version/namespaces/storage/limit_exclusions'; export const LIST_EXCLUSIONS_ENDPOINT = '/api/:version/namespaces/storage/limit_exclusions';
export const DELETE_EXCLUSION_ENDPOINT = '/api/:version/namespaces/:id/storage/limit_exclusion';
export const exclusionListFetchError = s__( export const exclusionListFetchError = s__(
'NamespaceLimits|There was an error fetching the exclusion list, try refreshing the page.', 'NamespaceLimits|There was an error fetching the exclusion list, try refreshing the page.',
...@@ -9,3 +10,21 @@ export const exclusionListFetchError = s__( ...@@ -9,3 +10,21 @@ export const exclusionListFetchError = s__(
export const excludedNamespacesDescription = s__( export const excludedNamespacesDescription = s__(
"NamespaceLimits|These namespaces won't receive any notifications nor any degraded functionality while they remain on this list", "NamespaceLimits|These namespaces won't receive any notifications nor any degraded functionality while they remain on this list",
); );
export const exclusionDeleteError = s__(
'NamespaceLimits|There was an error deleting the namespace: "%{errorMessage}".',
);
export const deleteModalTitle = s__('NamespaceLimits|Deletion confirmation');
export const deleteModalBody = s__(
'NamespaceLimits|Do you confirm the deletion of the selected namespace from the exclusion list?',
);
export const deleteModalProps = {
primaryProps: {
text: s__('NamespaceLimits|Confirm deletion'),
attributes: { variant: 'danger', category: 'primary' },
},
cancelProps: { text: __('Cancel') },
};
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { GlAlert, GlTable } from '@gitlab/ui'; import { GlAlert, GlTable, GlButton, GlModal } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import ExcludedNamespacesForm from 'ee/pages/admin/namespace_limits/components/excluded_namespaces_form.vue'; import ExcludedNamespacesForm from 'ee/pages/admin/namespace_limits/components/excluded_namespaces_form.vue';
import { import {
LIST_EXCLUSIONS_ENDPOINT, LIST_EXCLUSIONS_ENDPOINT,
DELETE_EXCLUSION_ENDPOINT,
exclusionListFetchError, exclusionListFetchError,
} from 'ee/pages/admin/namespace_limits/constants'; } from 'ee/pages/admin/namespace_limits/constants';
import ExcludedNamespaces from 'ee/pages/admin/namespace_limits/components/excluded_namespaces.vue'; import ExcludedNamespaces from 'ee/pages/admin/namespace_limits/components/excluded_namespaces.vue';
...@@ -16,15 +17,18 @@ describe('ExcludedNamespaces', () => { ...@@ -16,15 +17,18 @@ describe('ExcludedNamespaces', () => {
let wrapper; let wrapper;
let axiosMock; let axiosMock;
const listExclusionsEndpoint = LIST_EXCLUSIONS_ENDPOINT.replace(':version', 'v4'); const listExclusionsEndpoint = LIST_EXCLUSIONS_ENDPOINT.replace(':version', 'v4');
const deleteExclusionEndpoint = DELETE_EXCLUSION_ENDPOINT.replace(':version', 'v4');
const createComponent = ({ mountFn = shallowMount } = {}) => { const createComponent = ({ mountFn = shallowMount } = {}) => {
wrapper = mountFn(ExcludedNamespaces); wrapper = mountFn(ExcludedNamespaces);
wrapper.vm.$refs.modal.show = jest.fn();
}; };
const findForm = () => wrapper.findComponent(ExcludedNamespacesForm); const findForm = () => wrapper.findComponent(ExcludedNamespacesForm);
const findTable = () => wrapper.findComponent(GlTable); const findTable = () => wrapper.findComponent(GlTable);
const findTableRowCells = (row) => findTable().find('tbody').findAll('tr').at(row).findAll('td'); const findTableRowCells = (row) => findTable().find('tbody').findAll('tr').at(row).findAll('td');
const findAlert = () => wrapper.findComponent(GlAlert); const findAlert = () => wrapper.findComponent(GlAlert);
const findModal = () => wrapper.findComponent(GlModal);
beforeEach(() => { beforeEach(() => {
window.gon = { api_version: 'v4' }; window.gon = { api_version: 'v4' };
...@@ -58,6 +62,13 @@ describe('ExcludedNamespaces', () => { ...@@ -58,6 +62,13 @@ describe('ExcludedNamespaces', () => {
}); });
describe('exclusion table', () => { describe('exclusion table', () => {
it('calls the exclusion list endpoint on component mount', async () => {
axiosMock.onGet(listExclusionsEndpoint).replyOnce(HTTP_STATUS_OK, mockData);
createComponent();
await waitForPromises();
expect(axiosMock.history.get.length).toBe(1);
});
it('renders an error if there is a problem fetching the list', async () => { it('renders an error if there is a problem fetching the list', async () => {
axiosMock.onGet(listExclusionsEndpoint).replyOnce(HTTP_STATUS_BAD_REQUEST); axiosMock.onGet(listExclusionsEndpoint).replyOnce(HTTP_STATUS_BAD_REQUEST);
createComponent(); createComponent();
...@@ -78,7 +89,33 @@ describe('ExcludedNamespaces', () => { ...@@ -78,7 +89,33 @@ describe('ExcludedNamespaces', () => {
expect(cells.at(0).text()).toEqual(item.namespace_name); expect(cells.at(0).text()).toEqual(item.namespace_name);
expect(cells.at(1).text()).toEqual(`${item.namespace_id}`); expect(cells.at(1).text()).toEqual(`${item.namespace_id}`);
expect(cells.at(2).text()).toEqual(item.reason); expect(cells.at(2).text()).toEqual(item.reason);
expect(cells.at(3).findComponent(GlButton).text()).toBe('Delete');
}); });
}); });
}); });
describe('deleting exclusion', () => {
beforeEach(async () => {
axiosMock.onGet(listExclusionsEndpoint).replyOnce(HTTP_STATUS_OK, [mockData[0]]);
axiosMock.onDelete(deleteExclusionEndpoint).replyOnce(HTTP_STATUS_OK);
createComponent({ mountFn: mount });
await waitForPromises();
wrapper.findComponent(GlTable).findComponent(GlButton).trigger('click');
});
it('opens confirmation modal when delete button is clicked', () => {
expect(wrapper.vm.$refs.modal.show).toHaveBeenCalled();
expect(findModal().props()).toMatchObject({
title: 'Deletion confirmation',
actionPrimary: { text: 'Confirm deletion' },
});
});
it('sends deletion request to the backend when deletion modal is confirmed', async () => {
findModal().vm.$emit('primary');
await waitForPromises();
expect(axiosMock.history.delete.length).toBe(1);
});
});
}); });
...@@ -29623,9 +29623,18 @@ msgstr "" ...@@ -29623,9 +29623,18 @@ msgstr ""
msgid "NamespaceLimits|%{linkStart}%{username}%{linkEnd} changed the limit to %{limit} at %{date}" msgid "NamespaceLimits|%{linkStart}%{username}%{linkEnd} changed the limit to %{limit} at %{date}"
msgstr "" msgstr ""
   
msgid "NamespaceLimits|Confirm deletion"
msgstr ""
msgid "NamespaceLimits|Confirm limits change" msgid "NamespaceLimits|Confirm limits change"
msgstr "" msgstr ""
   
msgid "NamespaceLimits|Deletion confirmation"
msgstr ""
msgid "NamespaceLimits|Do you confirm the deletion of the selected namespace from the exclusion list?"
msgstr ""
msgid "NamespaceLimits|Enter a valid number greater or equal to zero." msgid "NamespaceLimits|Enter a valid number greater or equal to zero."
msgstr "" msgstr ""
   
...@@ -29650,6 +29659,9 @@ msgstr "" ...@@ -29650,6 +29659,9 @@ msgstr ""
msgid "NamespaceLimits|Storage Phased Notification" msgid "NamespaceLimits|Storage Phased Notification"
msgstr "" msgstr ""
   
msgid "NamespaceLimits|There was an error deleting the namespace: \"%{errorMessage}\"."
msgstr ""
msgid "NamespaceLimits|There was an error fetching the exclusion list, try refreshing the page." msgid "NamespaceLimits|There was an error fetching the exclusion list, try refreshing the page."
msgstr "" msgstr ""
   
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册