From ed5f2e34eab26e8a4d6c9bb2976269f5c4fd4116 Mon Sep 17 00:00:00 2001 From: Shane Maglangit Date: Mon, 20 Oct 2025 15:15:17 +0800 Subject: [PATCH 1/3] Show additional model information on Data management page Display creation date, last checksum verification, and storage size for each model item in an organized layout. --- .../admin/data_management/components/app.vue | 8 +-- .../components/data_management_item.vue | 53 +++++++++++++++++ .../data_management/components/app_spec.js | 11 ++-- .../components/data_management_item_spec.js | 58 +++++++++++++++++++ .../fixtures/admin/data_management.rb | 8 ++- locale/gitlab.pot | 6 ++ 6 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 ee/app/assets/javascripts/admin/data_management/components/data_management_item.vue create mode 100644 ee/spec/frontend/admin/data_management/components/data_management_item_spec.js diff --git a/ee/app/assets/javascripts/admin/data_management/components/app.vue b/ee/app/assets/javascripts/admin/data_management/components/app.vue index 53c83fd508a18f..8afe196ea66856 100644 --- a/ee/app/assets/javascripts/admin/data_management/components/app.vue +++ b/ee/app/assets/javascripts/admin/data_management/components/app.vue @@ -12,12 +12,14 @@ import { setUrlParams, visitUrl } from '~/lib/utils/url_utility'; import { createAlert } from '~/alert'; import { getModels } from 'ee/api/data_management_api'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import DataManagementItem from 'ee/admin/data_management/components/data_management_item.vue'; export default { name: 'AdminDataManagementApp', components: { GeoListTopBar, GeoList, + DataManagementItem, }, props: { modelClass: { @@ -96,11 +98,7 @@ export default { @listboxChange="handleListboxChange" /> - + diff --git a/ee/app/assets/javascripts/admin/data_management/components/data_management_item.vue b/ee/app/assets/javascripts/admin/data_management/components/data_management_item.vue new file mode 100644 index 00000000000000..1df7fdc21fbc04 --- /dev/null +++ b/ee/app/assets/javascripts/admin/data_management/components/data_management_item.vue @@ -0,0 +1,53 @@ + + + diff --git a/ee/spec/frontend/admin/data_management/components/app_spec.js b/ee/spec/frontend/admin/data_management/components/app_spec.js index 05a12d8d7d1a6c..d07db04f1fe981 100644 --- a/ee/spec/frontend/admin/data_management/components/app_spec.js +++ b/ee/spec/frontend/admin/data_management/components/app_spec.js @@ -10,6 +10,8 @@ import { TEST_HOST } from 'spec/test_constants'; import { getModels } from 'ee/api/data_management_api'; import waitForPromises from 'helpers/wait_for_promises'; import { createAlert } from '~/alert'; +import DataManagementItem from 'ee/admin/data_management/components/data_management_item.vue'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; jest.mock('~/lib/utils/url_utility', () => ({ ...jest.requireActual('~/lib/utils/url_utility'), @@ -31,6 +33,7 @@ describe('AdminDataManagementApp', () => { const findGeoListTopBar = () => wrapper.findComponent(GeoListTopBar); const findGeoList = () => wrapper.findComponent(GeoList); + const findDataManagementItem = () => wrapper.findComponent(DataManagementItem); beforeEach(() => { createComponent(); @@ -86,12 +89,10 @@ describe('AdminDataManagementApp', () => { }); it('renders items on GeoList', () => { - expect(findGeoList().props('hasItems')).toBe(true); - expect(findGeoList().findAll('li')).toHaveLength(models.length); + const [item] = convertObjectPropsToCamelCase(models, { deep: true }); - models.forEach((model) => { - expect(findGeoList().text()).toContain(`${model.record_identifier}`); - }); + expect(findGeoList().props('hasItems')).toBe(true); + expect(findDataManagementItem().props()).toMatchObject({ item }); }); it('stops loading state', () => { diff --git a/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js b/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js new file mode 100644 index 00000000000000..c979687e5c7954 --- /dev/null +++ b/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js @@ -0,0 +1,58 @@ +import { shallowMount } from '@vue/test-utils'; +import DataManagementItem from 'ee/admin/data_management/components/data_management_item.vue'; +import GeoListItem from 'ee/geo_shared/list/components/geo_list_item.vue'; +import models from 'test_fixtures/api/admin/data_management/snippet_repository.json'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; + +describe('DataManagementItem', () => { + let wrapper; + + const [model] = convertObjectPropsToCamelCase(models, { deep: true }); + + const createComponent = (props = {}) => { + wrapper = shallowMount(DataManagementItem, { + propsData: { + item: model, + ...props, + }, + }); + }; + + const findGeoListItem = () => wrapper.findComponent(GeoListItem); + + it('renders GeoListItem with correct props', () => { + createComponent(); + + expect(findGeoListItem().props()).toMatchObject({ + name: `${model.modelClass}/${model.recordIdentifier}`, + timeAgoArray: [ + { + label: 'Created', + dateString: model.createdAt, + defaultText: 'Unknown', + }, + { + label: 'Last checksum', + dateString: model.checksumInformation.lastChecksum, + defaultText: 'Unknown', + }, + ], + }); + }); + + describe('when fileSize is provided', () => { + it('renders size', () => { + createComponent({ item: { ...model, fileSize: 1024 } }); + + expect(findGeoListItem().text()).toContain('Storage: 1.00 KiB'); + }); + }); + + describe('when fileSize is not provided', () => { + it('renders size as "Unknown"', () => { + createComponent({ item: { ...model, fileSize: null } }); + + expect(findGeoListItem().text()).toContain('Storage: Unknown'); + }); + }); +}); diff --git a/ee/spec/frontend/fixtures/admin/data_management.rb b/ee/spec/frontend/fixtures/admin/data_management.rb index 17fd16f49f22c8..955a9e21ae7961 100644 --- a/ee/spec/frontend/fixtures/admin/data_management.rb +++ b/ee/spec/frontend/fixtures/admin/data_management.rb @@ -6,9 +6,15 @@ type: :request, feature_category: :geo_replication do include ApiHelpers include JavaScriptFixturesHelpers + include EE::GeoHelpers let_it_be(:admin) { create(:admin) } - let_it_be(:snippet_repository) { create(:snippet_repository) } + let_it_be(:node) { create(:geo_node) } + let_it_be(:snippet_repository) { create(:snippet_repository, :verification_succeeded) } + + before do + stub_current_geo_node(node) + end it 'api/admin/data_management/snippet_repository.json' do get api("/admin/data_management/snippet_repository", admin, admin_mode: true) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 4c078394ae47dd..d42dcd6dbf7d25 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -29550,6 +29550,9 @@ msgstr "" msgid "Geo|LFS Objects" msgstr "" +msgid "Geo|Last checksum" +msgstr "" + msgid "Geo|Last event ID" msgstr "" @@ -29883,6 +29886,9 @@ msgstr "" msgid "Geo|Storage config" msgstr "" +msgid "Geo|Storage: %{size}" +msgstr "" + msgid "Geo|Succeeded" msgstr "" -- GitLab From ccd3cc86ab5d98a623b340e3a51524896751565a Mon Sep 17 00:00:00 2001 From: Shane Maglangit Date: Mon, 20 Oct 2025 18:05:59 +0800 Subject: [PATCH 2/3] Fix eslint --- .../data_management/components/data_management_item_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js b/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js index c979687e5c7954..f8b05d51a59728 100644 --- a/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js +++ b/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js @@ -1,7 +1,7 @@ import { shallowMount } from '@vue/test-utils'; +import models from 'test_fixtures/api/admin/data_management/snippet_repository.json'; import DataManagementItem from 'ee/admin/data_management/components/data_management_item.vue'; import GeoListItem from 'ee/geo_shared/list/components/geo_list_item.vue'; -import models from 'test_fixtures/api/admin/data_management/snippet_repository.json'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; describe('DataManagementItem', () => { -- GitLab From 07a22f84efb0f415e94b82d23d23d87d626b3670 Mon Sep 17 00:00:00 2001 From: Shane Maglangit Date: Wed, 22 Oct 2025 11:23:14 +0800 Subject: [PATCH 3/3] Apply reviewer suggestion --- .../data_management/components/data_management_item.vue | 9 ++++++--- .../components/data_management_item_spec.js | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ee/app/assets/javascripts/admin/data_management/components/data_management_item.vue b/ee/app/assets/javascripts/admin/data_management/components/data_management_item.vue index 1df7fdc21fbc04..cd032a0142f9f9 100644 --- a/ee/app/assets/javascripts/admin/data_management/components/data_management_item.vue +++ b/ee/app/assets/javascripts/admin/data_management/components/data_management_item.vue @@ -6,7 +6,9 @@ import { numberToHumanSize } from '~/lib/utils/number_utils'; export default { i18n: { + created: __('Created'), unknown: __('Unknown'), + lastChecksum: s__('Geo|Last checksum'), }, components: { GeoListItem, @@ -21,12 +23,12 @@ export default { timeAgoArray() { return [ { - label: __('Created'), + label: this.$options.i18n.created, dateString: this.item.createdAt, defaultText: this.$options.i18n.unknown, }, { - label: s__('Geo|Last checksum'), + label: this.$options.i18n.lastChecksum, dateString: this.item.checksumInformation?.lastChecksum, defaultText: this.$options.i18n.unknown, }, @@ -37,9 +39,10 @@ export default { }, size() { const { fileSize } = this.item; + const hasFileSize = fileSize != null; return sprintf(s__('Geo|Storage: %{size}'), { - size: fileSize ? numberToHumanSize(fileSize) : this.$options.i18n.unknown, + size: hasFileSize ? numberToHumanSize(fileSize) : this.$options.i18n.unknown, }); }, }, diff --git a/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js b/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js index f8b05d51a59728..5c0fe42f310ec0 100644 --- a/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js +++ b/ee/spec/frontend/admin/data_management/components/data_management_item_spec.js @@ -48,6 +48,14 @@ describe('DataManagementItem', () => { }); }); + describe('when fileSize is 0', () => { + it('renders size"', () => { + createComponent({ item: { ...model, fileSize: 0 } }); + + expect(findGeoListItem().text()).toContain('Storage: 0 B'); + }); + }); + describe('when fileSize is not provided', () => { it('renders size as "Unknown"', () => { createComponent({ item: { ...model, fileSize: null } }); -- GitLab