Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronize zookeeper clusters with UI #1888

Merged
merged 17 commits into from
Aug 21, 2024
Merged
26 changes: 26 additions & 0 deletions hermes-console/json-server/db.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,32 @@
]
}
],
"inconsistentGroups3":[
{
"name": "pl.allegro.public.group",
"inconsistentMetadata": [],
"inconsistentTopics": [
{
"name": "pl.allegro.public.group.DummyEvent",
"inconsistentMetadata": [],
"inconsistentSubscriptions": [
{
"name": "pl.allegro.public.group.DummyEvent$foobar-service",
"inconsistentMetadata": [
{
"datacenter": "DC1"
},
{
"datacenter": "DC2",
"content": "{\n \"id\": \"foobar-service\",\n \"topicName\": \"pl.allegro.public.group.DummyEvent\",\n \"name\": \"foobar-service\",\n \"endpoint\": \"service://foobar-service/events/dummy-event\",\n \"state\": \"ACTIVE\",\n \"description\": \"Test Hermes endpoint\",\n \"subscriptionPolicy\": {\n \"rate\": 10,\n \"messageTtl\": 60,\n \"messageBackoff\": 100,\n \"requestTimeout\": 1000,\n \"socketTimeout\": 0,\n \"sendingDelay\": 0,\n \"backoffMultiplier\": 1.0,\n \"backoffMaxIntervalInSec\": 600,\n \"retryClientErrors\": true,\n \"backoffMaxIntervalMillis\": 600000\n },\n \"trackingEnabled\": false,\n \"trackingMode\": \"trackingOff\",\n \"owner\": {\n \"source\": \"Service Catalog\",\n \"id\": \"42\"\n },\n \"monitoringDetails\": {\n \"severity\": \"NON_IMPORTANT\",\n \"reaction\": \"\"\n },\n \"contentType\": \"JSON\",\n \"deliveryType\": \"SERIAL\",\n \"filters\": [\n {\n \"type\": \"avropath\",\n \"path\": \"foobar\",\n \"matcher\": \"^FOO_BAR$|^BAZ_BAR$\",\n \"matchingStrategy\": \"any\"\n },\n {\n \"type\": \"avropath\",\n \"path\": \".foo.bar.baz\",\n \"matcher\": \"true\",\n \"matchingStrategy\": \"all\"\n }\n ],\n \"mode\": \"ANYCAST\",\n \"headers\": [\n {\n \"name\": \"X-My-Header\",\n \"value\": \"boobar\"\n },\n {\n \"name\": \"X-Another-Header\",\n \"value\": \"foobar\"\n }\n ],\n \"endpointAddressResolverMetadata\": {\n \"additionalMetadata\": false,\n \"nonSupportedProperty\": 2\n },\n \"http2Enabled\": false,\n \"subscriptionIdentityHeadersEnabled\": false,\n \"autoDeleteWithTopicEnabled\": false,\n \"createdAt\": 1579507131.238,\n \"modifiedAt\": 1672140855.813\n}"
}
]
}
]
}
]
}
],
"topicNames": [
"pl.allegro.public.offer.product.ProductEventV1",
"pl.allegro.public.offer.product.ProductEventV2",
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 @@ -3,6 +3,7 @@
"/consistency/groups": "/consistencyGroups",
"/consistency/inconsistencies/groups?groupNames=pl.allegro.public.offer*": "/inconsistentGroups",
"/consistency/inconsistencies/groups?groupNames=pl.allegro.public.group2*": "/inconsistentGroups2",
"/consistency/inconsistencies/groups?groupNames=pl.allegro.public.group": "/inconsistentGroups3",
"/groups": "/groups",
"/owners/sources/Service%20Catalog/:id": "/topicsOwners/:id",
"/readiness/datacenters": "/readinessDatacenters",
Expand Down
16 changes: 16 additions & 0 deletions hermes-console/json-server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,22 @@ server.put(
},
);

server.post(
'/consistency/sync/topics/pl.allegro.public.group.DummyEvent/subscriptions/barbaz-service*',
(req, res) => {
res.sendStatus(200);
},
);

server.post(
'/consistency/sync/topics/pl.allegro.public.group.DummyEvent*',
(req, res) => {
res.status(404).jsonp({
message: 'Group pl.allegro.public.group not found',
});
},
);

server.post('/filters/:topic', (req, res) => {
res.jsonp(filterDebug);
});
Expand Down
42 changes: 42 additions & 0 deletions hermes-console/src/api/hermes-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,45 @@ export function verifyFilters(
},
);
}

export function syncGroup(
groupName: string,
primaryDatacenter: string,
): ResponsePromise<void> {
return axios.post<void>(`/consistency/sync/groups/${groupName}`, null, {
params: {
primaryDatacenter: primaryDatacenter,
},
});
}

export function syncTopic(
topicQualifiedName: string,
primaryDatacenter: string,
): ResponsePromise<void> {
return axios.post<void>(
`/consistency/sync/topics/${topicQualifiedName}`,
null,
{
params: {
primaryDatacenter: primaryDatacenter,
},
},
);
}

export function syncSubscription(
topicQualifiedName: string,
subscriptionName: string,
primaryDatacenter: string,
): ResponsePromise<void> {
return axios.post<void>(
`/consistency/sync/topics/${topicQualifiedName}/subscriptions/${subscriptionName}`,
null,
{
params: {
primaryDatacenter: primaryDatacenter,
},
},
);
}
175 changes: 175 additions & 0 deletions hermes-console/src/composables/sync/use-sync/useSync.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { afterEach, describe, expect } from 'vitest';
import { createTestingPinia } from '@pinia/testing';
import {
expectNotificationDispatched,
notificationStoreSpy,
} from '@/utils/test-utils';
import { setActivePinia } from 'pinia';
import { setupServer } from 'msw/node';
import {
syncGroupHandler,
syncSubscriptionHandler,
syncTopicHandler,
} from '@/mocks/handlers';
import { useSync } from '@/composables/sync/use-sync/useSync';
import { waitFor } from '@testing-library/vue';

describe('useSync', () => {
const server = setupServer();

const pinia = createTestingPinia({
fakeApp: true,
});

beforeEach(() => {
setActivePinia(pinia);
});

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

it('should show error notification when group sync fails', async () => {
// given
const groupName = 'group';
server.use(syncGroupHandler({ groupName, statusCode: 500 }));
server.listen();

const notificationStore = notificationStoreSpy();

// when
const { syncGroup } = useSync();
const result = await syncGroup(groupName, 'DC1');

// then
expect(result).toBeFalsy();

await waitFor(() => {
expectNotificationDispatched(notificationStore, {
type: 'error',
title: 'notifications.consistency.sync.failure',
});
});
});

it('should show error notification when topic sync fails', async () => {
// given
const topicName = 'group.topic';
server.use(syncTopicHandler({ topicName, statusCode: 500 }));
server.listen();

const notificationStore = notificationStoreSpy();

// when
const { syncTopic } = useSync();
const result = await syncTopic(topicName, 'DC1');

// then
expect(result).toBeFalsy();

await waitFor(() => {
expectNotificationDispatched(notificationStore, {
type: 'error',
title: 'notifications.consistency.sync.failure',
});
});
});

it('should show error notification when subscription sync fails', async () => {
// given
const topicName = 'group.topic';
const subscriptionName = 'subscription';
server.use(
syncSubscriptionHandler({ topicName, subscriptionName, statusCode: 500 }),
);
server.listen();

const notificationStore = notificationStoreSpy();

// when
const { syncSubscription } = useSync();
const result = await syncSubscription(topicName, subscriptionName, 'DC1');

// then
expect(result).toBeFalsy();

await waitFor(() => {
expectNotificationDispatched(notificationStore, {
type: 'error',
title: 'notifications.consistency.sync.failure',
});
});
});

it('should show success notification when group sync is successful', async () => {
const groupName = 'group';

server.use(syncGroupHandler({ groupName, statusCode: 200 }));
server.listen();

const notificationStore = notificationStoreSpy();

// when
const { syncGroup } = useSync();
const result = await syncGroup(groupName, 'DC1');

// then
expect(result).toBeTruthy();

await waitFor(() => {
expectNotificationDispatched(notificationStore, {
type: 'success',
text: 'notifications.consistency.sync.success',
});
});
});

it('should show success notification when topic sync is successful', async () => {
// given
const topicName = 'group.topic';
server.use(syncTopicHandler({ topicName, statusCode: 200 }));
server.listen();

const notificationStore = notificationStoreSpy();

// when
const { syncTopic } = useSync();
const result = await syncTopic(topicName, 'DC1');

// then
expect(result).toBeTruthy();

await waitFor(() => {
expectNotificationDispatched(notificationStore, {
type: 'success',
text: 'notifications.consistency.sync.success',
});
});
});

it('should show success notification when subscription sync is successful', async () => {
// given
const topicName = 'group.topic';
const subscriptionName = 'subscription';
server.use(
syncSubscriptionHandler({ topicName, subscriptionName, statusCode: 200 }),
);
server.listen();

const notificationStore = notificationStoreSpy();

// when
const { syncSubscription } = useSync();
const result = await syncSubscription(topicName, subscriptionName, 'DC1');

// then
expect(result).toBeTruthy();

await waitFor(() => {
expectNotificationDispatched(notificationStore, {
type: 'success',
text: 'notifications.consistency.sync.success',
});
});
});
});
Loading
Loading