Skip to content

Commit

Permalink
Feature/groups topics (#1702)
Browse files Browse the repository at this point in the history
Implemented groups and topic groups

Co-authored-by: Karol Szapsza <[email protected]>
  • Loading branch information
szczygiel-m and kszapsza authored Aug 10, 2023
1 parent 6e514ef commit ec27914
Show file tree
Hide file tree
Showing 72 changed files with 1,930 additions and 589 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-console.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Run frontend tests
run: yarn && yarn test:unit
- name: Run linter
Expand Down
2 changes: 2 additions & 0 deletions hermes-console-vue/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,7 @@ module.exports = {
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
},
],
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
},
};
24 changes: 24 additions & 0 deletions hermes-console-vue/json-server/db.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
{
"groups": [
"pl.allegro.public.offer",
"pl.allegro.public.offer.product",
"pl.allegro.public.order",
"pl.allegro.public.user",
"pl.allegro.public.group",
"pl.allegro.public.admin"
],
"topicNames": [
"pl.allegro.public.offer.product.ProductEventV1",
"pl.allegro.public.offer.product.ProductEventV2",
"pl.allegro.public.offer.product.ProductEventV3",
"pl.allegro.public.offer.product.ProductEventV4",
"pl.allegro.public.offer.OfferEventV1",
"pl.allegro.public.offer.OfferEventV2",
"pl.allegro.public.offer.OfferEventV3",
"pl.allegro.public.order.OrderEventV1",
"pl.allegro.public.order.OrderEventV2",
"pl.allegro.public.user.UserCreatedEvent",
"pl.allegro.public.user.UserChangedEvent",
"pl.allegro.public.group.DummyEvent",
"pl.allegro.public.admin.AdminOfferActionEvent",
"pl.allegro.public.admin.AdminOrderActionEvent"
],
"offlineClientsSource": {
"source": "https://www.openstreetmap.org/export/embed.html?bbox=-0.004017949104309083%2C51.47612752641776%2C0.00030577182769775396%2C51.478569861898606&layer=mapnik"
},
Expand Down
8 changes: 5 additions & 3 deletions hermes-console-vue/json-server/routes.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"/readiness/datacenters": "/readinessDatacenters",
"/workload-constraints": "/constraints",
"/consistency/inconsistencies/topics": "/inconsistentTopics",
"/groups": "/groups",
"/owners/sources/Service%20Catalog/:id": "/topicsOwners/:id",
"/readiness/datacenters": "/readinessDatacenters",
"/topics": "/topicNames",
"/topics/:id/metrics": "/topicsMetrics/:id",
"/topics/:id/preview": "/topicPreview",
"/topics/:id/offline-clients-source": "/offlineClientsSource",
Expand All @@ -12,5 +14,5 @@
"/topics/:topicName/subscriptions/:id/metrics": "/subscriptionsMetrics/:id",
"/topics/:topicName/subscriptions/:id/undelivered": "/subscriptionUndeliveredMessages",
"/topics/:topicName/subscriptions/:id/undelivered/last": "/subscriptionUndeliveredMessages/:id",
"/owners/sources/Service%20Catalog/:id": "/topicsOwners/:id"
"/workload-constraints": "/constraints"
}
3 changes: 3 additions & 0 deletions hermes-console-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"name": "hermes-console-vue",
"description": "Console for Hermes Management",
"license": "Apache-2.0",
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"dev": "vite",
"build": "run-p type-check build-only",
Expand Down
31 changes: 31 additions & 0 deletions hermes-console-vue/src/api/hermes-client/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import axios from 'axios';
import type { AppConfiguration } from '@/api/app-configuration';
import type { ConstraintsConfig } from '@/api/constraints';
import type { ConsumerGroup } from '@/api/consumer-group';
import type { DatacenterReadiness } from '@/api/datacenter-readiness';
import type {
MessagePreview,
TopicMetrics,
Expand Down Expand Up @@ -60,3 +63,31 @@ export function fetchOfflineClientsSource(
`/topics/${topicName}/offline-clients-source`,
);
}

export function fetchConstraints(): ResponsePromise<ConstraintsConfig> {
return axios.get<ConstraintsConfig>('/workload-constraints');
}

export function fetchReadiness(): ResponsePromise<DatacenterReadiness[]> {
return axios.get<DatacenterReadiness[]>('/readiness/datacenters');
}

export function fetchConsumerGroups(
topicName: string,
subscription: string,
): ResponsePromise<ConsumerGroup[]> {
return axios.get<ConsumerGroup[]>(
`/topics/${topicName}/subscriptions/${subscription}/consumer-groups`,
);
}

export function fetchInconsistentTopics(): ResponsePromise<string[]> {
return axios.get<string[]>('/consistency/inconsistencies/topics');
}

export function fetchTopicNames(): ResponsePromise<string[]> {
return axios.get<string[]>('/topics');
}
export function fetchGroupNames(): ResponsePromise<string[]> {
return axios.get<string[]>('/groups');
}
2 changes: 1 addition & 1 deletion hermes-console-vue/src/api/topic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export enum Ack {
}

export interface PublishingAuth {
publishers: string[];
publishers?: string[];
enabled: boolean;
unauthenticatedAccessEnabled: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<template>
<v-alert
:title="props.title"
:title="props.title ?? ''"
:text="props.text"
variant="elevated"
:type="props.type"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<v-row justify="center" no-gutters>
<v-btn
v-for="link in links"
:key="link"
:key="link.href"
variant="text"
class="mx-2"
rounded="xl"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,26 @@ describe('ThemeSwitch', () => {
['dark', 'light'],
])(
'should change Vuetify theme on button click (initial theme: %s)',
(initialTheme: string, changedTheme: string) => {
async (initialTheme: string, changedTheme: string) => {
// given
const { getByRole } = render(ThemeSwitch, { testVuetify });
testVuetify.theme.global.name.value = initialTheme;

// when
fireEvent.click(getByRole('button'));
await fireEvent.click(getByRole('button'));

// then
expect(testVuetify.theme.global.name.value).toBe(changedTheme);
},
);

it('should persist theme preference in local storage', () => {
it('should persist theme preference in local storage', async () => {
// given
const { getByRole } = render(ThemeSwitch, { testVuetify });
testVuetify.theme.global.name.value = 'light';

// when
fireEvent.click(getByRole('button'));
await fireEvent.click(getByRole('button'));

// then
expect(localStorage.getItem('hermes-console-theme')).toBe('dark');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
import { afterEach } from 'vitest';
import { dummyConstraints } from '@/dummy/constraints';
import { useConstraints } from '@/composables/use-constraints/useConstraints';
import {
fetchConstraintsErrorHandler,
fetchConstraintsHandler,
} from '@/mocks/handlers';
import { setupServer } from 'msw/node';
import { useConstraints } from '@/composables/constraints/use-constraints/useConstraints';
import { waitFor } from '@testing-library/vue';
import axios from 'axios';
import type { Mocked } from 'vitest';

vitest.mock('axios');
const mockedAxios = axios as Mocked<typeof axios>;

describe('useConstraints', () => {
it('should hit constraints Hermes API endpoint', async () => {
// given
mockedAxios.get.mockResolvedValueOnce({ data: [] });

// when
useConstraints();
const server = setupServer(
fetchConstraintsHandler({ constraints: dummyConstraints }),
);

// then
await waitFor(() => {
expect(mockedAxios.get.mock.calls[0][0]).toBe('/workload-constraints');
});
afterEach(() => {
server.resetHandlers();
});

it('should fetch constraints names from Hermes backend', async () => {
// given
mockedAxios.get.mockResolvedValueOnce({ data: dummyConstraints });
server.listen();

// when
const { topicConstraints, subscriptionConstraints, loading, error } =
useConstraints();

// then
expect(loading.value).toBe(true);
expect(error.value).toBe(false);
expect(loading.value).toBeTruthy();
expect(error.value.fetchConstraints).toBeNull();

await waitFor(() => {
expect(loading.value).toBe(false);
expect(error.value).toBe(false);
expect(loading.value).toBeFalsy();
expect(error.value.fetchConstraints).toBeNull();
expect(topicConstraints.value?.['pl.group.Topic1'].consumersNumber).toBe(
2,
);
Expand All @@ -48,14 +44,15 @@ describe('useConstraints', () => {

it('should set error to true on workload endpoint failure', async () => {
// given
mockedAxios.get.mockRejectedValueOnce({});
server.use(fetchConstraintsErrorHandler({ errorCode: 500 }));
server.listen();

// when
const { error } = useConstraints();

// then
await waitFor(() => {
expect(error.value).toBe(true);
expect(error.value.fetchConstraints).not.toBeNull();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { computed, ref } from 'vue';
import { fetchConstraints as getConstraints } from '@/api/hermes-client';
import type { Constraint, ConstraintsConfig } from '@/api/constraints';
import type { Ref } from 'vue';

export interface UseConstraints {
topicConstraints: Ref<Record<string, Constraint> | undefined>;
subscriptionConstraints: Ref<Record<string, Constraint> | undefined>;
loading: Ref<boolean>;
error: Ref<UseConstraintsErrors>;
}

export interface UseConstraintsErrors {
fetchConstraints: Error | null;
}

export function useConstraints(): UseConstraints {
const constraints = ref<ConstraintsConfig>();
const error = ref<UseConstraintsErrors>({
fetchConstraints: null,
});
const loading = ref(false);

const topicConstraints = computed((): Record<string, Constraint> => {
return constraints.value?.topicConstraints ?? {};
});

const subscriptionConstraints = computed((): Record<string, Constraint> => {
return constraints.value?.subscriptionConstraints ?? {};
});

const fetchConstraints = async () => {
try {
loading.value = true;
constraints.value = (await getConstraints()).data;
} catch (e) {
error.value.fetchConstraints = e as Error;
} finally {
loading.value = false;
}
};

fetchConstraints();

return {
topicConstraints,
subscriptionConstraints,
loading,
error,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { afterEach } from 'vitest';
import { dummyConsumerGroups } from '@/dummy/consumerGroups';
import { dummySubscription } from '@/dummy/subscription';
import { dummyTopic } from '@/dummy/topic';
import {
fetchConsumerGroupsErrorHandler,
fetchConsumerGroupsHandler,
} from '@/mocks/handlers';
import { setupServer } from 'msw/node';
import { useConsumerGroups } from '@/composables/consumer-groups/use-consumer-groups/useConsumerGroups';
import { waitFor } from '@testing-library/vue';

describe('useConsumerGroups', () => {
const topicName = dummyTopic.name;
const subscriptionName = dummySubscription.name;

const server = setupServer(
fetchConsumerGroupsHandler({
consumerGroups: dummyConsumerGroups,
topicName,
subscriptionName,
}),
);

afterEach(() => {
server.resetHandlers();
});

it('should fetch consumerGroups details from Hermes API', async () => {
// given
server.listen();

// when
const { consumerGroups, loading, error } = useConsumerGroups(
topicName,
subscriptionName,
);

// then
expect(loading.value).toBeTruthy();

await waitFor(() => {
expect(loading.value).toBeFalsy();
expect(error.value.fetchConsumerGroups).toBeNull();
expect(consumerGroups.value).toEqual(consumerGroups.value);
});
});

it('should set error to true on consumerGroups endpoint failure', async () => {
// given
server.use(
fetchConsumerGroupsErrorHandler({
errorCode: 500,
topicName,
subscriptionName,
}),
);
server.listen();

// when
const { loading, error } = useConsumerGroups(topicName, subscriptionName);

// then
await waitFor(() => {
expect(loading.value).toBeFalsy();
expect(error.value.fetchConsumerGroups).not.toBeNull();
});
});
});
Loading

0 comments on commit ec27914

Please sign in to comment.