diff --git a/hermes-console/json-server/db.json b/hermes-console/json-server/db.json index 12f97d5dd3..9cad6dac00 100644 --- a/hermes-console/json-server/db.json +++ b/hermes-console/json-server/db.json @@ -138,6 +138,11 @@ "offlineClientsSource": { "source": "https://www.openstreetmap.org/export/embed.html?bbox=-0.004017949104309083%2C51.47612752641776%2C0.00030577182769775396%2C51.478569861898606&layer=mapnik" }, + "topicClients": [ + "client1@allegro.com", + "client2@allegro.com", + "client3@allegro.com" + ], "inconsistentTopics": [ "pl.allegro.group.Topic1_avro", "pl.allegro.group.Topic2_avro", diff --git a/hermes-console/json-server/routes.json b/hermes-console/json-server/routes.json index 0a741b6490..cb9036178c 100644 --- a/hermes-console/json-server/routes.json +++ b/hermes-console/json-server/routes.json @@ -11,6 +11,7 @@ "/topics/:id/metrics": "/topicsMetrics/:id", "/topics/:id/preview": "/topicPreview", "/topics/:id/offline-clients-source": "/offlineClientsSource", + "/topics/:id/clients": "/topicClients", "/topics/:id/subscriptions": "/topicSubscriptions", "/topics/:topicName/subscriptions/:id": "/subscriptions/:id", "/topics/:topicName/subscriptions/:id/consumer-groups": "/consumerGroups", diff --git a/hermes-console/src/api/hermes-client/index.ts b/hermes-console/src/api/hermes-client/index.ts index 23b7ca55ef..a409b4a306 100644 --- a/hermes-console/src/api/hermes-client/index.ts +++ b/hermes-console/src/api/hermes-client/index.ts @@ -189,6 +189,12 @@ export function fetchOfflineClientsSource( ); } +export function fetchTopicClients( + topicName: string, +): ResponsePromise { + return axios.get(`/topics/${topicName}/clients`); +} + export function fetchConstraints(): ResponsePromise { return axios.get('/workload-constraints'); } diff --git a/hermes-console/src/composables/topic/use-topic/useTopic.spec.ts b/hermes-console/src/composables/topic/use-topic/useTopic.spec.ts index cf18cfb98e..cdb41acb08 100644 --- a/hermes-console/src/composables/topic/use-topic/useTopic.spec.ts +++ b/hermes-console/src/composables/topic/use-topic/useTopic.spec.ts @@ -16,6 +16,7 @@ import { } from '@/utils/test-utils'; import { fetchOwnerErrorHandler, + fetchTopicClientsErrorHandler, fetchTopicErrorHandler, fetchTopicMessagesPreviewErrorHandler, fetchTopicMetricsErrorHandler, @@ -207,6 +208,26 @@ describe('useTopic', () => { }); }); }); + + it('should show message that fetching clients was unsuccessful', async () => { + // given + server.use(fetchTopicClientsErrorHandler({ topicName: dummyTopic.name })); + server.listen(); + const notificationStore = notificationStoreSpy(); + + const { fetchTopicClients } = useTopic(topicName); + + // when + const clients = await fetchTopicClients(); + + // then + expect(clients).toBeNull(); + + expectNotificationDispatched(notificationStore, { + type: 'error', + title: 'notifications.topic.clients.fetch.failure', + }); + }); }); function expectErrors( diff --git a/hermes-console/src/composables/topic/use-topic/useTopic.ts b/hermes-console/src/composables/topic/use-topic/useTopic.ts index a0167e656a..e9dac5eaca 100644 --- a/hermes-console/src/composables/topic/use-topic/useTopic.ts +++ b/hermes-console/src/composables/topic/use-topic/useTopic.ts @@ -3,6 +3,7 @@ import { fetchTopic, fetchOfflineClientsSource as getOfflineClientsSource, fetchTopic as getTopic, + fetchTopicClients as getTopicClients, fetchTopicMessagesPreview as getTopicMessagesPreview, fetchTopicMetrics as getTopicMetrics, fetchOwner as getTopicOwner, @@ -35,6 +36,7 @@ export interface UseTopic { error: Ref; fetchOfflineClientsSource: () => Promise; removeTopic: () => Promise; + fetchTopicClients: () => Promise; } export interface UseTopicErrors { @@ -172,6 +174,19 @@ export function useTopic(topicName: string): UseTopic { } }; + const fetchTopicClients = async () => { + try { + return (await getTopicClients(topicName)).data; + } catch (e: any) { + dispatchErrorNotification( + e, + notificationStore, + useGlobalI18n().t('notifications.topic.clients.fetch.failure'), + ); + return null; + } + }; + fetchTopic(); return { @@ -185,6 +200,7 @@ export function useTopic(topicName: string): UseTopic { error, fetchOfflineClientsSource, removeTopic, + fetchTopicClients, }; } diff --git a/hermes-console/src/i18n/en-US/index.ts b/hermes-console/src/i18n/en-US/index.ts index 1b2447e162..58247ebd34 100644 --- a/hermes-console/src/i18n/en-US/index.ts +++ b/hermes-console/src/i18n/en-US/index.ts @@ -376,6 +376,7 @@ const en_US = { title: 'Subscriptions', create: 'Create subscription', search: 'Search...', + copy: 'Copy clients to clipboard', }, errorMessage: { topicFetchFailed: 'Could not fetch {topicName} topic details', @@ -710,6 +711,11 @@ const en_US = { success: 'Topic {topicName} successfully deleted', failure: "Couldn't delete topic {topicName}", }, + clients: { + fetch: { + failure: 'Failed to fetch topic clients', + }, + }, }, inconsistentTopic: { delete: { diff --git a/hermes-console/src/mocks/handlers.ts b/hermes-console/src/mocks/handlers.ts index ebcccbb847..b48712e238 100644 --- a/hermes-console/src/mocks/handlers.ts +++ b/hermes-console/src/mocks/handlers.ts @@ -185,6 +185,19 @@ export const fetchTopicSubscriptionDetailsErrorHandler = ({ }, ); +export const fetchTopicClientsErrorHandler = ({ + topicName, + errorCode = 500, +}: { + topicName: string; + errorCode?: number; +}) => + http.get(`${url}/topics/${topicName}/clients`, () => { + return new HttpResponse(undefined, { + status: errorCode, + }); + }); + export const successfulTopicHandlers = [ fetchTopicHandler({}), fetchOwnerHandler({}), diff --git a/hermes-console/src/views/topic/TopicView.vue b/hermes-console/src/views/topic/TopicView.vue index bacbc5db56..054afb19b6 100644 --- a/hermes-console/src/views/topic/TopicView.vue +++ b/hermes-console/src/views/topic/TopicView.vue @@ -1,4 +1,5 @@