Skip to content

Commit bc9ca89

Browse files
committed
feat: Add useTopics model hook and query for topics listing
- This commit adds a new `useTopics` model hook, which makes GraphQL requests to the server to administer Kafka topics. - The hook returns a set of sub-hooks, with just the `useGetTopics` sub-hook implemented for now which returns the list of topics (including their name, number of partitions and number of replicas). Contributes to: strimzi#124 Signed-off-by: Andrew Borley <[email protected]>
1 parent 2689890 commit bc9ca89

File tree

13 files changed

+201
-7
lines changed

13 files changed

+201
-7
lines changed

client/Bootstrap/index.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ import { init } from 'i18n';
88
import { ApolloProvider } from '@apollo/client';
99

1010
import { apolloClient } from 'Bootstrap/GraphQLClient';
11-
import { ConfigFeatureFlagProvider, FeatureFlag } from 'Contexts';
11+
import {
12+
ConfigFeatureFlagProvider,
13+
FeatureFlag,
14+
LoggingProvider,
15+
} from 'Contexts';
1216
import { Home } from 'Panels/Home';
13-
import { LoggingProvider } from 'Contexts';
1417

1518
init(); //Bootstrap i18next support
1619
ReactDOM.render(

client/Hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
*/
55
export * from './useConfigFeatureFlag/useConfigFeatureFlag';
66
export * from './useLogger';
7+
export * from './useTopics';

client/Hooks/useTopics/Hook.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright Strimzi authors.
3+
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
4+
*/
5+
import { GET_TOPICS } from 'Queries/Topics';
6+
import { useQuery } from '@apollo/client';
7+
import { useTopicsType } from './useTopics.types';
8+
9+
export const useTopics = (): useTopicsType => {
10+
const useGetTopics = () => useQuery(GET_TOPICS);
11+
return {
12+
useGetTopics,
13+
};
14+
};

client/Hooks/useTopics/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# useTopics
2+
3+
The `useTopics` hook returns sub-hooks to administer Kafka topics via GraphQL queries to the server `/api` endpoint. The sub-hooks returned by the `useTopics` hook are:
4+
5+
- `useGetTopics()` - returns the list of topics, including name, partitions count and replicas count, as an [Apollo `QueryResult` object](https://www.apollographql.com/docs/react/api/react/hooks/#result).

client/Hooks/useTopics/index.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*
2+
* Copyright Strimzi authors.
3+
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
4+
*/
5+
export * from './Hook';
6+
export * as useTopicsAssets from './useTopics.assets';
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright Strimzi authors.
3+
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
4+
*/
5+
import { generateMockDataResponseForGQLRequest } from 'utils/test/withApollo/withApollo.util';
6+
import { topicsType } from './useTopics.types';
7+
import { GET_TOPICS } from 'Queries/Topics';
8+
9+
export const mockClientResponse: topicsType = {
10+
topics: [
11+
{ name: 'testtopic1', partitions: 1, replicas: 1 },
12+
{ name: 'testtopic2', partitions: 2, replicas: 2 },
13+
{ name: 'testtopic3', partitions: 3, replicas: 3 },
14+
],
15+
};
16+
17+
const successRequest = generateMockDataResponseForGQLRequest(
18+
GET_TOPICS,
19+
mockClientResponse
20+
);
21+
22+
export const mockRequests = {
23+
successRequest,
24+
};
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright Strimzi authors.
3+
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
4+
*/
5+
import React from 'react';
6+
import { renderHook, act } from '@testing-library/react-hooks';
7+
import { withApolloProviderReturning, apolloMockResponse } from 'utils/test';
8+
import { useTopics } from './Hook';
9+
import { mockRequests, mockClientResponse } from './useTopics.assets';
10+
11+
describe('`useTopics` hook', () => {
12+
describe('`useGetTopics` hook', () => {
13+
it('returns the expected content', async () => {
14+
const { result, rerender } = renderHook(
15+
() => useTopics().useGetTopics(),
16+
{
17+
wrapper: ({ children }) =>
18+
withApolloProviderReturning(
19+
[mockRequests.successRequest],
20+
<React.Fragment>{children}</React.Fragment>
21+
),
22+
}
23+
);
24+
25+
expect(result.current.loading).toBe(true);
26+
expect(result.current.data).toBeUndefined();
27+
28+
await act(async () => {
29+
await apolloMockResponse();
30+
});
31+
rerender();
32+
33+
expect(result.current.loading).toBe(false);
34+
expect(result.current.data).toEqual(mockClientResponse);
35+
});
36+
});
37+
});
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright Strimzi authors.
3+
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
4+
*/
5+
import { QueryResult } from '@apollo/client';
6+
7+
/** the shape of the object returned by the useTopics hook */
8+
export type useTopicsType = {
9+
useGetTopics: () => QueryResult<topicsType>;
10+
};
11+
12+
/** the shape of the `data` returned by the getTopics function */
13+
export type topicsType = {
14+
topics: topicType[];
15+
};
16+
17+
export type topicType = {
18+
name: string;
19+
partitions: number;
20+
replicas: number;
21+
};

client/Models/README.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Models
2+
3+
This directory contains custom [React Hooks](https://reactjs.org/docs/hooks-intro.html#motivation). Unlike the [Hooks](../Hooks/README.md), these hooks encapsulate specific business logic for views to utilise.
4+
5+
## Test approach
6+
7+
Elements should be tested in a functional manor. See [Test Driven Development](../../docs/Test.md#style-of-test).
8+
9+
## Expected files
10+
11+
For a given model `useFoo`, the expected files are as follows:
12+
13+
```
14+
Models/
15+
index.ts
16+
types.ts
17+
useFoo/
18+
README.md
19+
useFoo.ts
20+
useFoo.spec.ts
21+
useFoo.assets.ts
22+
useFoo.types.ts
23+
```
24+
25+
Where:
26+
27+
- index.ts acts as a barrel file, exporting the hooks defined in the Hooks directory
28+
- types.ts acts as a barrel file, exporting all the public types of each context
29+
- README.md is the readme for this hook, detailing design choices and usage
30+
- useFoo.ts is the hook implementation
31+
- useFoo.spec.ts are the tests for this hook
32+
- useFoo.assets.ts are the test assets for this hook
33+
- useFoo.types.ts are the types for this hook
34+
35+
## Implemented hooks
36+
37+
- [`useTopics`](./useTopics/README.md) - a hook providing sub-hooks to administer Kafka topics via GraphQL queries.

client/Models/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
* Copyright Strimzi authors.
3+
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
4+
*/
5+
// export * from './useTopics';

client/Panels/Home/Home.steps.tsx

+22-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
import { Given, When, Then, Fusion } from 'jest-cucumber-fusion';
66
import { RenderResult } from '@testing-library/react';
77
import merge from 'lodash.merge';
8-
import { renderWithCustomConfigFeatureFlagContext } from 'utils/test';
8+
import {
9+
renderWithCustomConfigFeatureFlagContext,
10+
withApolloProviderReturning,
11+
apolloMockResponse,
12+
} from 'utils/test';
13+
import { useTopicsAssets } from 'Hooks';
914
import { Home } from '.';
1015
import React, { ReactElement } from 'react';
1116

@@ -31,7 +36,10 @@ Given('a Home component', () => {
3136
When('it is rendered', () => {
3237
renderResult = renderWithCustomConfigFeatureFlagContext(
3338
coreConfigFromContext,
34-
component
39+
withApolloProviderReturning(
40+
[useTopicsAssets.mockRequests.successRequest],
41+
component
42+
)
3543
);
3644
showVersionSet = true;
3745
});
@@ -47,18 +55,28 @@ When('it is rendered with no version', () => {
4755
},
4856
},
4957
}),
50-
component
58+
withApolloProviderReturning(
59+
[useTopicsAssets.mockRequests.successRequest],
60+
component
61+
)
5162
);
5263
showVersionSet = false;
5364
});
5465

55-
Then('it should display the expected text', () => {
66+
Then('it should display the expected text', async () => {
5667
const { getByText, queryByText } = renderResult;
5768
expect(getByText('Welcome to the Strimzi UI')).toBeInTheDocument();
5869
const versionString = `Version: ${coreConfigFromContext.client.about.version}`;
5970
showVersionSet
6071
? expect(getByText(versionString)).toBeInTheDocument()
6172
: expect(queryByText(versionString)).not.toBeInTheDocument();
73+
74+
const loadingTopicsString = 'Loading...';
75+
expect(getByText(loadingTopicsString)).toBeInTheDocument();
76+
77+
await apolloMockResponse();
78+
expect(queryByText(loadingTopicsString)).not.toBeInTheDocument();
79+
expect(getByText('Number of topics: 3')).toBeInTheDocument();
6280
});
6381

6482
Fusion('Home.feature');

client/Panels/Home/Home.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import React, { FunctionComponent } from 'react';
66
import get from 'lodash.get';
77
import image from 'Images/logo.png';
88
import './style.scss';
9-
import { useConfigFeatureFlag, useLogger } from 'Hooks';
9+
import { useConfigFeatureFlag, useLogger, useTopics } from 'Hooks';
1010

1111
const Home: FunctionComponent = ({ children }) => {
1212
const { client, featureFlags, isComplete } = useConfigFeatureFlag();
@@ -17,11 +17,19 @@ const Home: FunctionComponent = ({ children }) => {
1717
const { debug } = useLogger('Home');
1818
debug(`Client version to display: ${version}`);
1919

20+
const { useGetTopics } = useTopics();
21+
const { data, loading } = useGetTopics();
22+
2023
return (
2124
<div className='home'>
2225
<img src={image} alt='Strimzi logo' />
2326
<p>Welcome to the Strimzi UI</p>
2427
{showVersion && isComplete && <p>{`Version: ${version}`}</p>}
28+
{loading || !data ? (
29+
<p>Loading...</p>
30+
) : (
31+
<p>{`Number of topics: ${data.topics.length}`}</p>
32+
)}
2533
{children}
2634
</div>
2735
);

client/Queries/Topics/index.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright Strimzi authors.
3+
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
4+
*/
5+
import gql from 'graphql-tag';
6+
7+
export const GET_TOPICS = gql`
8+
query {
9+
topics {
10+
name
11+
partitions
12+
replicas
13+
}
14+
}
15+
`;

0 commit comments

Comments
 (0)