Skip to content

Commit

Permalink
resolve #1923 | Add a button to copy clients to clipboard (#1935)
Browse files Browse the repository at this point in the history
  • Loading branch information
ClouddJR authored Dec 19, 2024
1 parent aff7824 commit 7adcd41
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 0 deletions.
5 changes: 5 additions & 0 deletions hermes-console/json-server/db.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
"[email protected]",
"[email protected]",
"[email protected]"
],
"inconsistentTopics": [
"pl.allegro.group.Topic1_avro",
"pl.allegro.group.Topic2_avro",
Expand Down
1 change: 1 addition & 0 deletions hermes-console/json-server/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 6 additions & 0 deletions hermes-console/src/api/hermes-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ export function fetchOfflineClientsSource(
);
}

export function fetchTopicClients(
topicName: string,
): ResponsePromise<string[]> {
return axios.get(`/topics/${topicName}/clients`);
}

export function fetchConstraints(): ResponsePromise<ConstraintsConfig> {
return axios.get<ConstraintsConfig>('/workload-constraints');
}
Expand Down
21 changes: 21 additions & 0 deletions hermes-console/src/composables/topic/use-topic/useTopic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from '@/utils/test-utils';
import {
fetchOwnerErrorHandler,
fetchTopicClientsErrorHandler,
fetchTopicErrorHandler,
fetchTopicMessagesPreviewErrorHandler,
fetchTopicMetricsErrorHandler,
Expand Down Expand Up @@ -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(
Expand Down
16 changes: 16 additions & 0 deletions hermes-console/src/composables/topic/use-topic/useTopic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
fetchTopic,
fetchOfflineClientsSource as getOfflineClientsSource,
fetchTopic as getTopic,
fetchTopicClients as getTopicClients,
fetchTopicMessagesPreview as getTopicMessagesPreview,
fetchTopicMetrics as getTopicMetrics,
fetchOwner as getTopicOwner,
Expand Down Expand Up @@ -35,6 +36,7 @@ export interface UseTopic {
error: Ref<UseTopicErrors>;
fetchOfflineClientsSource: () => Promise<void>;
removeTopic: () => Promise<boolean>;
fetchTopicClients: () => Promise<string[] | null>;
}

export interface UseTopicErrors {
Expand Down Expand Up @@ -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 {
Expand All @@ -185,6 +200,7 @@ export function useTopic(topicName: string): UseTopic {
error,
fetchOfflineClientsSource,
removeTopic,
fetchTopicClients,
};
}

Expand Down
6 changes: 6 additions & 0 deletions hermes-console/src/i18n/en-US/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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: {
Expand Down
13 changes: 13 additions & 0 deletions hermes-console/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({}),
Expand Down
11 changes: 11 additions & 0 deletions hermes-console/src/views/topic/TopicView.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script async setup lang="ts">
import { copyToClipboard } from '@/utils/copy-utils';
import { isTopicOwnerOrAdmin } from '@/utils/roles-util';
import { useAppConfigStore } from '@/store/app-config/useAppConfigStore';
import { useDialog } from '@/composables/dialog/use-dialog/useDialog';
Expand Down Expand Up @@ -38,6 +39,7 @@
offlineClientsSource,
fetchOfflineClientsSource,
removeTopic,
fetchTopicClients,
} = useTopic(topicName);
const breadcrumbsItems = [
Expand Down Expand Up @@ -83,6 +85,14 @@
}
}
async function copyClientsToClipboard() {
const clients = await fetchTopicClients();
if (clients != null) {
copyToClipboard(clients.join(','));
}
}
function resolveCostsUrl(url?: string): string {
return url?.replace('{{topic_name}}', topicName) ?? '';
}
Expand Down Expand Up @@ -158,6 +168,7 @@
:topic-name="topicName"
:subscriptions="subscriptions ? subscriptions : []"
:roles="roles"
@copyClientsClick="copyClientsToClipboard"
/>

<offline-clients
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,32 @@ describe('SubscriptionsList', () => {
expect(queryByText('bazbar-service')).not.toBeInTheDocument();
},
);

it('should render copy clients button when there are subscriptions', async () => {
// when
const { getByText } = render(SubscriptionsList, { props });
await fireEvent.click(getByText('topicView.subscriptions.title (2)'));

// then
expect(getByText('topicView.subscriptions.copy')).toBeVisible();
});

it('should not render copy clients button when there are no subscriptions', async () => {
// given
const propsWithoutSubscriptions = {
groupId: 'pl.allegro',
topicName: 'pl.allegro.DummyTopic',
subscriptions: [],
roles: dummyRoles,
};

// when
const { getByText, queryByText } = render(SubscriptionsList, {
props: propsWithoutSubscriptions,
});
await fireEvent.click(getByText('topicView.subscriptions.title (0)'));

// then
expect(queryByText('topicView.subscriptions.copy')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
roles: Role[] | undefined;
}>();
const emit = defineEmits<{
copyClientsClick: [];
}>();
const statusTextColor: Record<State, String> = {
[State.ACTIVE]: 'green',
[State.PENDING]: 'orange',
Expand Down Expand Up @@ -79,6 +83,16 @@
v-model="filter"
prepend-inner-icon="mdi-magnify"
/>
<v-btn
v-if="subscriptions.length > 0"
:disabled="!isAny(roles)"
prepend-icon="mdi-content-copy"
density="comfortable"
@click="emit('copyClientsClick')"
style="margin-right: 10px"
>
{{ $t('topicView.subscriptions.copy') }}
</v-btn>
<v-btn
:disabled="!isAny(roles)"
prepend-icon="mdi-plus"
Expand Down

0 comments on commit 7adcd41

Please sign in to comment.