diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index c0a5de2d34..448adf7f91 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -22,14 +22,14 @@ "dependencies": { "@backstage/catalog-model": "^0.6.0", "@backstage/core": "^0.4.4", - "@backstage/theme": "^0.2.2", "@backstage/plugin-catalog": "^0.2.7", + "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", - "react-router": "6.0.0-beta.0", "react-dom": "^16.13.1", + "react-router": "6.0.0-beta.0", "react-use": "^15.3.3" }, "devDependencies": { @@ -38,10 +38,12 @@ "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", + "@testing-library/react-hooks": "^3.4.2", "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "cross-fetch": "^3.0.6", + "jest-when": "^3.1.0", "msw": "^0.21.2" }, "files": [ diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.test.tsx b/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.test.tsx index 629f7a9a7a..ea0f7e1ddb 100644 --- a/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.test.tsx +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.test.tsx @@ -17,26 +17,17 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import { ConsumerGroupOffsets } from './ConsumerGroupOffsets'; +import * as data from './__fixtures__/consumer-group-offsets.json'; +import { ConsumerGroupOffsetsResponse } from '../../api/types'; + +const consumerGroupOffsets = data as ConsumerGroupOffsetsResponse; describe('ConsumerGroupOffsets', () => { it('should render consumer group table', async () => { const rendered = await renderInTestApp( {}} />, diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.tsx b/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.tsx index 0f697e4994..448d420bc0 100644 --- a/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.tsx +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/ConsumerGroupOffsets.tsx @@ -18,8 +18,7 @@ import { Table, TableColumn } from '@backstage/core'; import { Box, Typography } from '@material-ui/core'; import RetryIcon from '@material-ui/icons/Replay'; import React from 'react'; -import { useConsumerGroupFromEntity } from './useConsumerGroupsFromEntity'; -import { useConsumerGroupOffsets } from './useConsumerGroupOffsets'; +import { useConsumerGroupsOffsetsForEntity } from './useConsumerGroupsOffsetsForEntity'; export type TopicPartitionInfo = { topic: string; @@ -98,14 +97,7 @@ export const ConsumerGroupOffsets = ({ }; export const KafkaTopicsForConsumer = () => { - const consumerGroup = useConsumerGroupFromEntity(); - const [tableProps, { retry }] = useConsumerGroupOffsets(consumerGroup); + const [tableProps, { retry }] = useConsumerGroupsOffsetsForEntity(); - return ( - - ); + return ; }; diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/__fixtures__/consumer-group-offsets.json b/plugins/kafka/src/components/ConsumerGroupOffsets/__fixtures__/consumer-group-offsets.json new file mode 100644 index 0000000000..ed4b81c066 --- /dev/null +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/__fixtures__/consumer-group-offsets.json @@ -0,0 +1,17 @@ +{ + "consumerId": "consumer", + "offsets": [ + { + "topic": "topic1", + "partitionId": 1, + "topicOffset": "100", + "groupOffset": "50" + }, + { + "topic": "topic2", + "partitionId": 1, + "topicOffset": "2340", + "groupOffset": "1234" + } + ] +} diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsForEntity.test.tsx b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsForEntity.test.tsx new file mode 100644 index 0000000000..44693d5bbc --- /dev/null +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsForEntity.test.tsx @@ -0,0 +1,53 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { PropsWithChildren } from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import { useConsumerGroupsForEntity } from './useConsumerGroupsForEntity'; +import { EntityContext } from '@backstage/plugin-catalog'; +import { Entity } from '@backstage/catalog-model'; + +describe('useConsumerGroupOffsets', () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'test', + annotations: { + 'kafka.apache.org/consumer-groups': 'consumer', + }, + }, + spec: { + owner: 'guest', + type: 'Website', + lifecycle: 'development', + }, + }; + + const wrapper = ({ children }: PropsWithChildren<{}>) => { + return ( + + {children} + + ); + }; + + const subject = () => renderHook(useConsumerGroupsForEntity, { wrapper }); + + it('returns correct consumer group for annotation', async () => { + const { result } = subject(); + expect(result.current).toBe('consumer'); + }); +}); diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsFromEntity.ts b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsForEntity.ts similarity index 94% rename from plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsFromEntity.ts rename to plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsForEntity.ts index 8bad96b61c..5003c2f5b5 100644 --- a/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsFromEntity.ts +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsForEntity.ts @@ -17,7 +17,7 @@ import { useEntity } from '@backstage/plugin-catalog'; import { KAFKA_CONSUMER_GROUP_ANNOTATION } from '../../constants'; -export const useConsumerGroupFromEntity = () => { +export const useConsumerGroupsForEntity = () => { const { entity } = useEntity(); return entity.metadata.annotations?.[KAFKA_CONSUMER_GROUP_ANNOTATION] ?? ''; diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsOffsetsForEntity.test.tsx b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsOffsetsForEntity.test.tsx new file mode 100644 index 0000000000..28f0ff7c28 --- /dev/null +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsOffsetsForEntity.test.tsx @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { PropsWithChildren } from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import { EntityContext } from '@backstage/plugin-catalog'; +import { Entity } from '@backstage/catalog-model'; +import * as data from './__fixtures__/consumer-group-offsets.json'; +import { + ConsumerGroupOffsetsResponse, + KafkaApi, + kafkaApiRef, +} from '../../api/types'; +import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core'; +import { useConsumerGroupsOffsetsForEntity } from './useConsumerGroupsOffsetsForEntity'; +import { when } from 'jest-when'; + +const consumerGroupOffsets = data as ConsumerGroupOffsetsResponse; + +const mockErrorApi: jest.Mocked = { + post: jest.fn(), + error$: jest.fn(), +}; + +const mockKafkaApi: jest.Mocked = { + getConsumerGroupOffsets: jest.fn(), +}; + +describe('useConsumerGroupOffsets', () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'test', + annotations: { + 'kafka.apache.org/consumer-groups': consumerGroupOffsets.consumerId, + }, + }, + spec: { + owner: 'guest', + type: 'Website', + lifecycle: 'development', + }, + }; + + const wrapper = ({ children }: PropsWithChildren<{}>) => { + return ( + + + {children} + + + ); + }; + + const subject = () => + renderHook(useConsumerGroupsOffsetsForEntity, { wrapper }); + + it('returns correct consumer group for annotation', async () => { + when(mockKafkaApi.getConsumerGroupOffsets) + .calledWith(consumerGroupOffsets.consumerId) + .mockResolvedValue(consumerGroupOffsets); + + const { result, waitForNextUpdate } = subject(); + await waitForNextUpdate(); + const [tableProps] = result.current; + + expect(tableProps.consumerGroup).toBe(consumerGroupOffsets.consumerId); + expect(tableProps.topics).toBe(consumerGroupOffsets.offsets); + }); + + it('posts an error to the error api', async () => { + const error = new Error('error!'); + mockKafkaApi.getConsumerGroupOffsets.mockRejectedValueOnce(error); + + const { waitForNextUpdate } = subject(); + await waitForNextUpdate(); + + expect(mockErrorApi.post).toHaveBeenCalledWith(error); + }); +}); diff --git a/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupOffsets.ts b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsOffsetsForEntity.ts similarity index 77% rename from plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupOffsets.ts rename to plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsOffsetsForEntity.ts index 74d408fe86..eb7e105989 100644 --- a/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupOffsets.ts +++ b/plugins/kafka/src/components/ConsumerGroupOffsets/useConsumerGroupsOffsetsForEntity.ts @@ -17,28 +17,31 @@ import { errorApiRef, useApi } from '@backstage/core'; import { useAsyncRetry } from 'react-use'; import { kafkaApiRef } from '../../api/types'; +import { useConsumerGroupsForEntity } from './useConsumerGroupsForEntity'; -export function useConsumerGroupOffsets(groupId: string) { +export const useConsumerGroupsOffsetsForEntity = () => { + const consumerGroup = useConsumerGroupsForEntity(); const api = useApi(kafkaApiRef); const errorApi = useApi(errorApiRef); const { loading, value: topics, retry } = useAsyncRetry(async () => { try { - const response = await api.getConsumerGroupOffsets(groupId); + const response = await api.getConsumerGroupOffsets(consumerGroup); return response.offsets; } catch (e) { errorApi.post(e); throw e; } - }, [api, errorApi, groupId]); + }, [api, errorApi, consumerGroup]); return [ { loading, + consumerGroup, topics, }, { retry, }, ] as const; -} +};